[USER32]: Fix MSVC compilation.
[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: 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 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
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 /* internal flags for menu tracking */
26
27 #define TF_ENDMENU 0x10000
28 #define TF_SUSPENDPOPUP 0x20000
29 #define TF_SKIPREMOVE 0x40000
30
31 #define ITEM_PREV -1
32 #define ITEM_NEXT 1
33
34 /* Internal MenuTrackMenu() flags */
35 #define TPM_INTERNAL 0xF0000000
36 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
37 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
38
39 /* top and bottom margins for popup menus */
40 #define MENU_TOP_MARGIN 3
41 #define MENU_BOTTOM_MARGIN 2
42
43 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
44
45 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
46
47 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
48 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
49 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
50
51 #define IS_SYSTEM_MENU(MenuInfo) \
52 (0 == ((MenuInfo)->Flags & MNF_POPUP) && 0 != ((MenuInfo)->Flags & MNF_SYSDESKMN))
53
54 #define IS_SYSTEM_POPUP(MenuInfo) \
55 (0 != ((MenuInfo)->Flags & MNF_POPUP) && 0 != ((MenuInfo)->Flags & MNF_SYSDESKMN))
56
57 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
58
59 /* Use global popup window because there's no way 2 menus can
60 * be tracked at the same time. */
61 static HWND top_popup;
62 static HMENU top_popup_hmenu;
63
64 /* Flag set by EndMenu() to force an exit from menu tracking */
65 static BOOL fEndMenu = FALSE;
66
67 #define MENU_ITEM_HBMP_SPACE (5)
68 #define MENU_BAR_ITEMS_SPACE (12)
69 #define SEPARATOR_HEIGHT (5)
70 #define MENU_TAB_SPACE (8)
71
72 typedef struct
73 {
74 UINT TrackFlags;
75 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
76 HMENU TopMenu; /* initial menu */
77 HWND OwnerWnd; /* where notifications are sent */
78 POINT Pt;
79 } MTRACKER;
80
81
82 /*********************************************************************
83 * PopupMenu class descriptor
84 */
85 const struct builtin_class_descr POPUPMENU_builtin_class =
86 {
87 WC_MENU, /* name */
88 CS_SAVEBITS | CS_DBLCLKS, /* style */
89 (WNDPROC) NULL, /* FIXME - procA */
90 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
91 sizeof(MENUINFO *), /* extra */
92 (LPCWSTR) IDC_ARROW, /* cursor */
93 (HBRUSH)(COLOR_MENU + 1) /* brush */
94 };
95
96 #ifndef GET_WORD
97 #define GET_WORD(ptr) (*(WORD *)(ptr))
98 #endif
99 #ifndef GET_DWORD
100 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
101 #endif
102
103 HFONT hMenuFont = NULL;
104 HFONT hMenuFontBold = NULL;
105
106 /* Dimension of the menu bitmaps */
107 static HBITMAP BmpSysMenu = NULL;
108
109 static SIZE MenuCharSize;
110
111 /***********************************************************************
112 * MenuGetRosMenuInfo
113 *
114 * Get full information about menu
115 */
116 static BOOL FASTCALL
117 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
118 {
119 MenuInfo->cbSize = sizeof(ROSMENUINFO);
120 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
121
122 return NtUserMenuInfo(Menu, MenuInfo, FALSE);
123 }
124
125 /***********************************************************************
126 * MenuSetRosMenuInfo
127 *
128 * Set full information about menu
129 */
130 static BOOL FASTCALL
131 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
132 {
133 MenuInfo->cbSize = sizeof(ROSMENUINFO);
134 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
135
136 return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE);
137 }
138
139 /***********************************************************************
140 * MenuInitRosMenuItemInfo
141 *
142 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
143 */
144 static VOID FASTCALL
145 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
146 {
147 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
148 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
149 }
150
151 /***********************************************************************
152 * MenuGetRosMenuItemInfo
153 *
154 * Get full information about a menu item
155 */
156 static BOOL FASTCALL
157 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
158 {
159 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
160
161 if (ItemInfo->dwTypeData != NULL)
162 {
163 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
164 }
165
166
167 ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
168 | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE;
169 ItemInfo->dwTypeData = NULL;
170
171 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
172 {
173 ItemInfo->fType = 0;
174 return FALSE;
175 }
176
177 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
178 {
179 ItemInfo->cch++;
180 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0,
181 ItemInfo->cch * sizeof(WCHAR));
182 if (NULL == ItemInfo->dwTypeData)
183 {
184 return FALSE;
185 }
186
187 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
188 {
189 ItemInfo->fType = 0;
190 return FALSE;
191 }
192 ItemInfo->dwTypeData[ItemInfo->cch - 1] = UNICODE_NULL;
193 }
194 ItemInfo->fMask = Save_Mask;
195 return TRUE;
196 }
197
198 /***********************************************************************
199 * MenuSetRosMenuItemInfo
200 *
201 * Set selected information about a menu item, need to set the mask bits.
202 */
203 static BOOL FASTCALL
204 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
205 {
206 BOOL Ret;
207
208 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
209 ItemInfo->dwTypeData != NULL)
210 {
211 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
212 }
213 Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE);
214
215 return Ret;
216 }
217
218 /***********************************************************************
219 * MenuCleanupRosMenuItemInfo
220 *
221 * Cleanup after use of MenuGet/SetRosMenuItemInfo
222 */
223 static VOID FASTCALL
224 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
225 {
226 if (ItemInfo->dwTypeData != NULL)
227 {
228 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
229 ItemInfo->dwTypeData = NULL;
230 }
231 }
232
233 /***********************************************************************
234 * MenuGetAllRosMenuItemInfo
235 *
236 * Get full information about all menu items
237 */
238 static INT FASTCALL
239 MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
240 {
241 DWORD BufSize;
242
243 BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
244 if (BufSize == (DWORD) -1 || BufSize == 0)
245 {
246 return -1;
247 }
248 *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
249 if (NULL == *ItemInfo)
250 {
251 return -1;
252 }
253
254 return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
255 }
256
257 /***********************************************************************
258 * MenuCleanupAllRosMenuItemInfo
259 *
260 * Cleanup after use of MenuGetAllRosMenuItemInfo
261 */
262 static VOID FASTCALL
263 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
264 {
265 HeapFree(GetProcessHeap(), 0, ItemInfo);
266 }
267
268 /***********************************************************************
269 * MenuInitSysMenuPopup
270 *
271 * Grey the appropriate items in System menu.
272 */
273 void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
274 {
275 BOOL gray;
276 UINT DefItem;
277 #if 0
278 MENUITEMINFOW mii;
279 #endif
280
281 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
282 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
283 gray = ((style & WS_MAXIMIZE) != 0);
284 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
285 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
286 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
287 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
288 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
289 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
290 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
291 gray = (clsStyle & CS_NOCLOSE) != 0;
292
293 /* The menu item must keep its state if it's disabled */
294 if(gray)
295 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
296
297 /* Set default menu item */
298 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
299 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
300 else DefItem = SC_CLOSE;
301 #if 0
302 mii.cbSize = sizeof(MENUITEMINFOW);
303 mii.fMask |= MIIM_STATE;
304 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
305 (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
306 #endif
307 SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
308 }
309
310 /******************************************************************************
311 *
312 * UINT MenuGetStartOfNextColumn(
313 * PROSMENUINFO MenuInfo)
314 *
315 *****************************************************************************/
316 static UINT MenuGetStartOfNextColumn(
317 PROSMENUINFO MenuInfo)
318 {
319 PROSMENUITEMINFO MenuItems;
320 UINT i;
321
322 i = MenuInfo->FocusedItem;
323 if ( i == NO_SELECTED_ITEM )
324 return i;
325
326 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
327 return NO_SELECTED_ITEM;
328
329 for (i++ ; i < MenuInfo->MenuItemCount; i++)
330 if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
331 return i;
332
333 return NO_SELECTED_ITEM;
334 }
335
336 /******************************************************************************
337 *
338 * UINT MenuGetStartOfPrevColumn(
339 * PROSMENUINFO MenuInfo)
340 *
341 *****************************************************************************/
342
343 static UINT FASTCALL MenuGetStartOfPrevColumn(
344 PROSMENUINFO MenuInfo)
345 {
346 PROSMENUITEMINFO MenuItems;
347 UINT i;
348
349 if (!MenuInfo->FocusedItem || MenuInfo->FocusedItem == NO_SELECTED_ITEM)
350 return NO_SELECTED_ITEM;
351
352 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
353 return NO_SELECTED_ITEM;
354
355 /* Find the start of the column */
356 for (i = MenuInfo->FocusedItem;
357 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
358 --i)
359 {
360 ; /* empty */
361 }
362
363 if (i == 0)
364 {
365 MenuCleanupAllRosMenuItemInfo(MenuItems);
366 return NO_SELECTED_ITEM;
367 }
368
369 for (--i; 0 != i; --i)
370 if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
371 break;
372
373 MenuCleanupAllRosMenuItemInfo(MenuItems);
374 TRACE("ret %d.\n", i );
375
376 return i;
377 }
378
379 /***********************************************************************
380 * MenuFindSubMenu
381 *
382 * Find a Sub menu. Return the position of the submenu, and modifies
383 * *hmenu in case it is found in another sub-menu.
384 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
385 */
386 static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
387 {
388 ROSMENUINFO menu;
389 UINT i;
390 ROSMENUITEMINFO item;
391 if (((*hmenu)==(HMENU)0xffff) ||
392 (!MenuGetRosMenuInfo(&menu, *hmenu)))
393 return NO_SELECTED_ITEM;
394
395 MenuInitRosMenuItemInfo(&item);
396 for (i = 0; i < menu.MenuItemCount; i++) {
397 if (! MenuGetRosMenuItemInfo(menu.Self, i, &item))
398 {
399 MenuCleanupRosMenuItemInfo(&item);
400 return NO_SELECTED_ITEM;
401 }
402 if (!(item.hSubMenu)) continue;
403 if (item.hSubMenu == hSubTarget) {
404 MenuCleanupRosMenuItemInfo(&item);
405 return i;
406 }
407 else {
408 HMENU hsubmenu = item.hSubMenu;
409 UINT pos = MenuFindSubMenu(&hsubmenu, hSubTarget );
410 if (pos != NO_SELECTED_ITEM) {
411 *hmenu = hsubmenu;
412 return pos;
413 }
414 }
415 }
416 MenuCleanupRosMenuItemInfo(&item);
417 return NO_SELECTED_ITEM;
418 }
419
420 /***********************************************************************
421 * MenuLoadBitmaps
422 *
423 * Load the arrow bitmap. We can't do this from MenuInit since user32
424 * can also be used (and thus initialized) from text-mode.
425 */
426 static void FASTCALL
427 MenuLoadBitmaps(VOID)
428 {
429 /* Load system buttons bitmaps */
430 if (NULL == BmpSysMenu)
431 {
432 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
433 }
434 }
435
436 /***********************************************************************
437 * MenuDrawPopupGlyph
438 *
439 * Draws popup magic glyphs (can be found in system menu).
440 */
441 static void FASTCALL
442 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
443 {
444 LOGFONTW lf;
445 HFONT hFont, hOldFont;
446 COLORREF clrsave;
447 INT bkmode;
448 TCHAR symbol;
449 switch (popupMagic)
450 {
451 case (INT_PTR) HBMMENU_POPUP_RESTORE:
452 symbol = '2';
453 break;
454 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
455 symbol = '0';
456 break;
457 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
458 symbol = '1';
459 break;
460 case (INT_PTR) HBMMENU_POPUP_CLOSE:
461 symbol = 'r';
462 break;
463 default:
464 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
465 return;
466 }
467 ZeroMemory(&lf, sizeof(LOGFONTW));
468 InflateRect(r, -2, -2);
469 lf.lfHeight = r->bottom - r->top;
470 lf.lfWidth = 0;
471 lf.lfWeight = FW_NORMAL;
472 lf.lfCharSet = DEFAULT_CHARSET;
473 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
474 hFont = CreateFontIndirect(&lf);
475 /* save font and text color */
476 hOldFont = SelectObject(dc, hFont);
477 clrsave = GetTextColor(dc);
478 bkmode = GetBkMode(dc);
479 /* set color and drawing mode */
480 SetBkMode(dc, TRANSPARENT);
481 if (inactive)
482 {
483 /* draw shadow */
484 if (!hilite)
485 {
486 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
487 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
488 }
489 }
490 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
491 /* draw selected symbol */
492 TextOut(dc, r->left, r->top, &symbol, 1);
493 /* restore previous settings */
494 SetTextColor(dc, clrsave);
495 SelectObject(dc, hOldFont);
496 SetBkMode(dc, bkmode);
497 DeleteObject(hFont);
498 }
499
500 /***********************************************************************
501 * MenuFindItemByKey
502 *
503 * Find the menu item selected by a key press.
504 * Return item id, -1 if none, -2 if we should close the menu.
505 */
506 static UINT FASTCALL MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
507 WCHAR Key, BOOL ForceMenuChar)
508 {
509 ROSMENUINFO SysMenuInfo;
510 PROSMENUITEMINFO Items, ItemInfo;
511 LRESULT MenuChar;
512 UINT i;
513 WORD Flags = 0;
514
515 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
516
517 if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
518 {
519 if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
520 {
521 MenuInfo = &SysMenuInfo;
522 }
523 else
524 {
525 MenuInfo = NULL;
526 }
527 }
528
529 if (NULL != MenuInfo)
530 {
531 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
532 {
533 return -1;
534 }
535 if ( !ForceMenuChar )
536 {
537 ItemInfo = Items;
538 for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
539 {
540 if ((ItemInfo->lpstr) && NULL != ItemInfo->dwTypeData)
541 {
542 WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
543 do
544 {
545 p = strchrW (p + 2, '&');
546 }
547 while (p != NULL && p [1] == '&');
548 if (p && (toupperW(p[1]) == toupperW(Key))) return i;
549 }
550 }
551 }
552
553 Flags |= MenuInfo->Flags & MNF_POPUP ? MF_POPUP : 0;
554 Flags |= MenuInfo->Flags & MNF_SYSDESKMN ? MF_SYSMENU : 0;
555
556 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
557 MAKEWPARAM(Key, Flags), (LPARAM) MenuInfo->Self);
558 if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
559 if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
560 }
561 return (UINT)(-1);
562 }
563
564 /***********************************************************************
565 * MenuGetBitmapItemSize
566 *
567 * Get the size of a bitmap item.
568 */
569 static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
570 {
571 BITMAP bm;
572 HBITMAP bmp = lpitem->hbmpItem;
573
574 size->cx = size->cy = 0;
575
576 /* check if there is a magic menu item associated with this item */
577 if (IS_MAGIC_BITMAP(bmp))
578 {
579 switch((INT_PTR) bmp)
580 {
581 case (INT_PTR)HBMMENU_CALLBACK:
582 {
583 MEASUREITEMSTRUCT measItem;
584 measItem.CtlType = ODT_MENU;
585 measItem.CtlID = 0;
586 measItem.itemID = lpitem->wID;
587 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
588 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
589 measItem.itemData = lpitem->dwItemData;
590 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
591 size->cx = measItem.itemWidth;
592 size->cy = measItem.itemHeight;
593 return;
594 }
595 break;
596
597 case (INT_PTR) HBMMENU_SYSTEM:
598 if (0 != lpitem->dwItemData)
599 {
600 bmp = (HBITMAP) lpitem->dwItemData;
601 break;
602 }
603 /* fall through */
604 case (INT_PTR) HBMMENU_MBAR_RESTORE:
605 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
606 case (INT_PTR) HBMMENU_MBAR_CLOSE:
607 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
608 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
609 case (INT_PTR) HBMMENU_POPUP_CLOSE:
610 case (INT_PTR) HBMMENU_POPUP_RESTORE:
611 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
612 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
613 /* FIXME: Why we need to subtract these magic values? */
614 /* to make them smaller than the menu bar? */
615 size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
616 size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
617 return;
618 }
619 }
620
621 if (GetObjectW(bmp, sizeof(BITMAP), &bm))
622 {
623 size->cx = bm.bmWidth;
624 size->cy = bm.bmHeight;
625 }
626 }
627
628 /***********************************************************************
629 * MenuDrawBitmapItem
630 *
631 * Draw a bitmap item.
632 */
633 static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
634 HMENU hmenu, HWND WndOwner, UINT odaction, BOOL MenuBar)
635 {
636 BITMAP bm;
637 DWORD rop;
638 HDC hdcMem;
639 HBITMAP bmp;
640 int w = rect->right - rect->left;
641 int h = rect->bottom - rect->top;
642 int bmp_xoffset = 0;
643 int left, top;
644 HBITMAP hbmToDraw = lpitem->hbmpItem;
645 bmp = hbmToDraw;
646
647 /* Check if there is a magic menu item associated with this item */
648 if (IS_MAGIC_BITMAP(hbmToDraw))
649 {
650 UINT flags = 0;
651 RECT r;
652
653 r = *rect;
654 switch ((INT_PTR)hbmToDraw)
655 {
656 case (INT_PTR)HBMMENU_SYSTEM:
657 if (lpitem->dwTypeData)
658 {
659 bmp = (HBITMAP)lpitem->dwTypeData;
660 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
661 }
662 else
663 {
664 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
665 bmp = BmpSysMenu;
666 if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
667 /* only use right half of the bitmap */
668 bmp_xoffset = bm.bmWidth / 2;
669 bm.bmWidth -= bmp_xoffset;
670 }
671 goto got_bitmap;
672 case (INT_PTR)HBMMENU_MBAR_RESTORE:
673 flags = DFCS_CAPTIONRESTORE;
674 break;
675 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
676 r.right += 1;
677 flags = DFCS_CAPTIONMIN;
678 break;
679 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
680 r.right += 1;
681 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
682 break;
683 case (INT_PTR)HBMMENU_MBAR_CLOSE:
684 flags = DFCS_CAPTIONCLOSE;
685 break;
686 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
687 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
688 break;
689 case (INT_PTR)HBMMENU_CALLBACK:
690 {
691 DRAWITEMSTRUCT drawItem;
692 POINT origorg;
693 drawItem.CtlType = ODT_MENU;
694 drawItem.CtlID = 0;
695 drawItem.itemID = lpitem->wID;
696 drawItem.itemAction = odaction;
697 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
698 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
699 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
700 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
701 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
702 drawItem.hwndItem = (HWND)hmenu;
703 drawItem.hDC = hdc;
704 drawItem.rcItem = *rect;
705 drawItem.itemData = lpitem->dwItemData;
706 /* some applications make this assumption on the DC's origin */
707 SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
708 OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
709 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
710 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
711 return;
712 }
713 break;
714
715 case (INT_PTR) HBMMENU_POPUP_CLOSE:
716 case (INT_PTR) HBMMENU_POPUP_RESTORE:
717 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
718 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
719 MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
720 return;
721 }
722 InflateRect(&r, -1, -1);
723 if (0 != (lpitem->fState & MF_HILITE))
724 {
725 flags |= DFCS_PUSHED;
726 }
727 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
728 return;
729 }
730
731 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
732
733 got_bitmap:
734 hdcMem = CreateCompatibleDC( hdc );
735 SelectObject( hdcMem, bmp );
736
737 /* handle fontsize > bitmap_height */
738 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
739 left=rect->left;
740 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
741 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
742 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
743 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
744 DeleteDC( hdcMem );
745 }
746
747 /***********************************************************************
748 * MenuCalcItemSize
749 *
750 * Calculate the size of the menu item and store it in lpitem->rect.
751 */
752 static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
753 INT orgX, INT orgY, BOOL menuBar)
754 {
755 WCHAR *p;
756 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
757 INT itemheight = 0;
758
759 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
760
761 MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
762
763 SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
764
765 if (lpitem->fType & MF_OWNERDRAW)
766 {
767 MEASUREITEMSTRUCT mis;
768 mis.CtlType = ODT_MENU;
769 mis.CtlID = 0;
770 mis.itemID = lpitem->wID;
771 mis.itemData = lpitem->dwItemData;
772 mis.itemHeight = HIWORD( GetDialogBaseUnits());
773 mis.itemWidth = 0;
774 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
775 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
776 * width of a menufont character to the width of an owner-drawn menu.
777 */
778 lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
779
780 if (menuBar) {
781 /* under at least win95 you seem to be given a standard
782 height for the menu and the height value is ignored */
783 lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
784 } else
785 lpitem->Rect.bottom += mis.itemHeight;
786
787 TRACE("id=%04lx size=%dx%d\n",
788 lpitem->wID, mis.itemWidth, mis.itemHeight);
789 return;
790 }
791
792 if (lpitem->fType & MF_SEPARATOR)
793 {
794 lpitem->Rect.bottom += SEPARATOR_HEIGHT;
795 if( !menuBar)
796 lpitem->Rect.right += check_bitmap_width + MenuCharSize.cx;
797 return;
798 }
799
800 lpitem->dxTab = 0;
801
802 if (lpitem->hbmpItem)
803 {
804 SIZE size;
805
806 if (!menuBar) {
807 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
808 /* Keep the size of the bitmap in callback mode to be able
809 * to draw it correctly */
810 lpitem->Rect.right = lpitem->Rect.left + size.cx;
811 if (MenuInfo->maxBmpSize.cx < abs(size.cx) + MENU_ITEM_HBMP_SPACE ||
812 MenuInfo->maxBmpSize.cy < abs(size.cy))
813 {
814 MenuInfo->maxBmpSize.cx = abs(size.cx) + MENU_ITEM_HBMP_SPACE;
815 MenuInfo->maxBmpSize.cy = abs(size.cy);
816 }
817 MenuSetRosMenuInfo(MenuInfo);
818 itemheight = size.cy + 2;
819
820 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
821 lpitem->Rect.right += 2 * check_bitmap_width;
822 lpitem->Rect.right += 4 + MenuCharSize.cx;
823 lpitem->dxTab = lpitem->Rect.right;
824 lpitem->Rect.right += check_bitmap_width;
825 } else /* hbmpItem & MenuBar */ {
826 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
827 lpitem->Rect.right += size.cx;
828 if( lpitem->lpstr) lpitem->Rect.right += 2;
829 itemheight = size.cy;
830
831 /* Special case: Minimize button doesn't have a space behind it. */
832 if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
833 lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
834 lpitem->Rect.right -= 1;
835 }
836 }
837 else if (!menuBar) {
838 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
839 lpitem->Rect.right += check_bitmap_width;
840 lpitem->Rect.right += 4 + MenuCharSize.cx;
841 lpitem->dxTab = lpitem->Rect.right;
842 lpitem->Rect.right += check_bitmap_width;
843 }
844
845 /* it must be a text item - unless it's the system menu */
846 if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
847 HFONT hfontOld = NULL;
848 RECT rc = lpitem->Rect;
849 LONG txtheight, txtwidth;
850
851 if ( lpitem->fState & MFS_DEFAULT ) {
852 hfontOld = SelectObject( hdc, hMenuFontBold );
853 }
854 if (menuBar) {
855 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
856 DT_SINGLELINE|DT_CALCRECT);
857 lpitem->Rect.right += rc.right - rc.left;
858 itemheight = max( max( itemheight, txtheight),
859 GetSystemMetrics( SM_CYMENU) - 1);
860 lpitem->Rect.right += 2 * MenuCharSize.cx;
861 } else {
862 if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
863 RECT tmprc = rc;
864 LONG tmpheight;
865 int n = (int)( p - lpitem->dwTypeData);
866 /* Item contains a tab (only meaningful in popup menus) */
867 /* get text size before the tab */
868 txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
869 DT_SINGLELINE|DT_CALCRECT);
870 txtwidth = rc.right - rc.left;
871 p += 1; /* advance past the Tab */
872 /* get text size after the tab */
873 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
874 DT_SINGLELINE|DT_CALCRECT);
875 lpitem->dxTab += txtwidth;
876 txtheight = max( txtheight, tmpheight);
877 txtwidth += MenuCharSize.cx + /* space for the tab */
878 tmprc.right - tmprc.left; /* space for the short cut */
879 } else {
880 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
881 DT_SINGLELINE|DT_CALCRECT);
882 txtwidth = rc.right - rc.left;
883 lpitem->dxTab += txtwidth;
884 }
885 lpitem->Rect.right += 2 + txtwidth;
886 itemheight = max( itemheight,
887 max( txtheight + 2, MenuCharSize.cy + 4));
888 }
889 if (hfontOld) SelectObject (hdc, hfontOld);
890 } else if( menuBar) {
891 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
892 }
893 lpitem->Rect.bottom += itemheight;
894 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
895 }
896
897 /***********************************************************************
898 * MenuPopupMenuCalcSize
899 *
900 * Calculate the size of a popup menu.
901 */
902 static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
903 {
904 ROSMENUITEMINFO lpitem;
905 HDC hdc;
906 int start, i;
907 int orgX, orgY, maxX, maxTab, maxTabWidth;
908
909 MenuInfo->Width = MenuInfo->Height = 0;
910 if (MenuInfo->MenuItemCount == 0)
911 {
912 MenuSetRosMenuInfo(MenuInfo);
913 return;
914 }
915
916 hdc = GetDC(NULL);
917 SelectObject( hdc, hMenuFont );
918
919 start = 0;
920 maxX = 2 + 1;
921
922 MenuInfo->maxBmpSize.cx = 0;
923 MenuInfo->maxBmpSize.cy = 0;
924
925 MenuInitRosMenuItemInfo(&lpitem);
926 while (start < MenuInfo->MenuItemCount)
927 {
928 orgX = maxX;
929 orgY = 2;
930
931 maxTab = maxTabWidth = 0;
932
933 /* Parse items until column break or end of menu */
934 for (i = start; i < MenuInfo->MenuItemCount; i++)
935 {
936 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
937 {
938 MenuCleanupRosMenuItemInfo(&lpitem);
939 MenuSetRosMenuInfo(MenuInfo);
940 return;
941 }
942 if (i != start &&
943 (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
944
945 MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE);
946 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
947 {
948 MenuCleanupRosMenuItemInfo(&lpitem);
949 MenuSetRosMenuInfo(MenuInfo);
950 return;
951 }
952 // Not sure here,, The patch from wine removes this.
953 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
954 // {
955 // OrgX++;
956 // }
957 maxX = max(maxX, lpitem.Rect.right);
958 orgY = lpitem.Rect.bottom;
959 if ((lpitem.lpstr) && lpitem.dxTab )
960 {
961 maxTab = max( maxTab, lpitem.dxTab );
962 maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
963 }
964 }
965
966 /* Finish the column (set all items to the largest width found) */
967 maxX = max( maxX, maxTab + maxTabWidth );
968 while (start < i)
969 {
970 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
971 {
972 lpitem.Rect.right = maxX;
973 if ((lpitem.lpstr) && 0 != lpitem.dxTab)
974 {
975 lpitem.dxTab = maxTab;
976 }
977 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
978 }
979 start++;
980 }
981 MenuInfo->Height = max(MenuInfo->Height, orgY);
982 }
983
984 MenuInfo->Width = maxX;
985
986 /* space for 3d border */
987 MenuInfo->Height += 2;
988 MenuInfo->Width += 2;
989
990 MenuCleanupRosMenuItemInfo(&lpitem);
991 MenuSetRosMenuInfo(MenuInfo);
992 ReleaseDC( 0, hdc );
993 }
994
995 /***********************************************************************
996 * MenuMenuBarCalcSize
997 *
998 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
999 * height is off by 1 pixel which causes lengthy window relocations when
1000 * active document window is maximized/restored.
1001 *
1002 * Calculate the size of the menu bar.
1003 */
1004 static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
1005 PROSMENUINFO MenuInfo, HWND hwndOwner )
1006 {
1007 ROSMENUITEMINFO ItemInfo;
1008 int start, i, orgX, orgY, maxY, helpPos;
1009
1010 if ((lprect == NULL) || (MenuInfo == NULL)) return;
1011 if (MenuInfo->MenuItemCount == 0) return;
1012 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect->left, lprect->top, lprect->right, lprect->bottom);
1013 MenuInfo->Width = lprect->right - lprect->left;
1014 MenuInfo->Height = 0;
1015 maxY = lprect->top + 1;
1016 start = 0;
1017 helpPos = -1;
1018
1019 MenuInfo->maxBmpSize.cx = 0;
1020 MenuInfo->maxBmpSize.cy = 0;
1021
1022 MenuInitRosMenuItemInfo(&ItemInfo);
1023 while (start < MenuInfo->MenuItemCount)
1024 {
1025 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1026 {
1027 MenuCleanupRosMenuItemInfo(&ItemInfo);
1028 return;
1029 }
1030 orgX = lprect->left;
1031 orgY = maxY;
1032
1033 /* Parse items until line break or end of menu */
1034 for (i = start; i < MenuInfo->MenuItemCount; i++)
1035 {
1036 if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
1037 if ((i != start) &&
1038 (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1039
1040 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
1041 MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE);
1042 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1043 {
1044 MenuCleanupRosMenuItemInfo(&ItemInfo);
1045 return;
1046 }
1047
1048 if (ItemInfo.Rect.right > lprect->right)
1049 {
1050 if (i != start) break;
1051 else ItemInfo.Rect.right = lprect->right;
1052 }
1053 maxY = max( maxY, ItemInfo.Rect.bottom );
1054 orgX = ItemInfo.Rect.right;
1055 if (i + 1 < MenuInfo->MenuItemCount)
1056 {
1057 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1058 {
1059 MenuCleanupRosMenuItemInfo(&ItemInfo);
1060 return;
1061 }
1062 }
1063 }
1064
1065 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1066 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1067 #if 0
1068 /* Finish the line (set all items to the largest height found) */
1069 while (start < i)
1070 {
1071 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1072 {
1073 ItemInfo.Rect.bottom = maxY;
1074 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
1075 }
1076 start++;
1077 }
1078 #else
1079 start = i; /* This works! */
1080 #endif
1081 }
1082
1083 lprect->bottom = maxY;
1084 MenuInfo->Height = lprect->bottom - lprect->top;
1085 MenuSetRosMenuInfo(MenuInfo);
1086
1087 if (helpPos != -1)
1088 {
1089 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1090 /* the last item (if several lines, only move the last line) */
1091 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
1092 {
1093 MenuCleanupRosMenuItemInfo(&ItemInfo);
1094 return;
1095 }
1096 orgY = ItemInfo.Rect.top;
1097 orgX = lprect->right;
1098 for (i = MenuInfo->MenuItemCount - 1; helpPos <= i; i--)
1099 {
1100 if (i < helpPos)
1101 {
1102 break; /* done */
1103 }
1104 if (ItemInfo.Rect.top != orgY)
1105 {
1106 break; /* Other line */
1107 }
1108 if (orgX <= ItemInfo.Rect.right)
1109 {
1110 break; /* Too far right already */
1111 }
1112 ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
1113 ItemInfo.Rect.right = orgX;
1114 orgX = ItemInfo.Rect.left;
1115 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1116 if (helpPos + 1 <= i &&
1117 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1118 {
1119 MenuCleanupRosMenuItemInfo(&ItemInfo);
1120 return;
1121 }
1122 }
1123 }
1124
1125 MenuCleanupRosMenuItemInfo(&ItemInfo);
1126 }
1127
1128 /***********************************************************************
1129 * MenuDrawMenuItem
1130 *
1131 * Draw a single menu item.
1132 */
1133 static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
1134 PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
1135 {
1136 RECT rect;
1137 PWCHAR Text;
1138 BOOL flat_menu = FALSE;
1139 int bkgnd;
1140 PWND Wnd = ValidateHwnd(hWnd);
1141
1142 if (!Wnd)
1143 return;
1144
1145 if (lpitem->fType & MF_SYSMENU)
1146 {
1147 if ( (Wnd->style & WS_MINIMIZE))
1148 {
1149 UserGetInsideRectNC(Wnd, &rect);
1150 UserDrawSysMenuButton(hWnd, hdc, &rect,
1151 lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
1152 }
1153 return;
1154 }
1155
1156 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1157 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1158
1159 /* Setup colors */
1160
1161 if (lpitem->fState & MF_HILITE)
1162 {
1163 if(menuBar && !flat_menu) {
1164 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1165 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1166 } else {
1167 if (lpitem->fState & MF_GRAYED)
1168 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1169 else
1170 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1171 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1172 }
1173 }
1174 else
1175 {
1176 if (lpitem->fState & MF_GRAYED)
1177 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1178 else
1179 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1180 SetBkColor( hdc, GetSysColor( bkgnd ) );
1181 }
1182
1183 rect = lpitem->Rect;
1184
1185 if (lpitem->fType & MF_OWNERDRAW)
1186 {
1187 /*
1188 ** Experimentation under Windows reveals that an owner-drawn
1189 ** menu is given the rectangle which includes the space it requested
1190 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1191 ** and a popup-menu arrow. This is the value of lpitem->rect.
1192 ** Windows will leave all drawing to the application except for
1193 ** the popup-menu arrow. Windows always draws that itself, after
1194 ** the menu owner has finished drawing.
1195 */
1196 DRAWITEMSTRUCT dis;
1197
1198 dis.CtlType = ODT_MENU;
1199 dis.CtlID = 0;
1200 dis.itemID = lpitem->wID;
1201 dis.itemData = (DWORD)lpitem->dwItemData;
1202 dis.itemState = 0;
1203 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1204 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
1205 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1206 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1207 dis.hwndItem = (HWND) MenuInfo->Self;
1208 dis.hDC = hdc;
1209 dis.rcItem = rect;
1210 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1211 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
1212 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1213 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1214 dis.rcItem.bottom);
1215 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
1216 /* Draw the popup-menu arrow */
1217 if (lpitem->hSubMenu)
1218 {
1219 RECT rectTemp;
1220 CopyRect(&rectTemp, &rect);
1221 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1222 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1223 }
1224 return;
1225 }
1226
1227 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1228
1229 if (lpitem->fState & MF_HILITE)
1230 {
1231 if (flat_menu)
1232 {
1233 InflateRect (&rect, -1, -1);
1234 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1235 InflateRect (&rect, 1, 1);
1236 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1237 }
1238 else
1239 {
1240 if(menuBar)
1241 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1242 else
1243 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1244 }
1245 }
1246 else
1247 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1248
1249 SetBkMode( hdc, TRANSPARENT );
1250
1251 /* vertical separator */
1252 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1253 {
1254 HPEN oldPen;
1255 RECT rc = rect;
1256
1257 rc.left -= 3;
1258 rc.top = 3;
1259 rc.bottom = Height - 3;
1260 if (flat_menu)
1261 {
1262 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1263 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
1264 MoveToEx( hdc, rc.left, rc.top, NULL );
1265 LineTo( hdc, rc.left, rc.bottom );
1266 SelectObject( hdc, oldPen );
1267 }
1268 else
1269 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1270 }
1271
1272 /* horizontal separator */
1273 if (lpitem->fType & MF_SEPARATOR)
1274 {
1275 HPEN oldPen;
1276 RECT rc = rect;
1277
1278 rc.left++;
1279 rc.right--;
1280 rc.top += SEPARATOR_HEIGHT / 2;
1281 if (flat_menu)
1282 {
1283 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1284 SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
1285 MoveToEx( hdc, rc.left, rc.top, NULL );
1286 LineTo( hdc, rc.right, rc.top );
1287 SelectObject( hdc, oldPen );
1288 }
1289 else
1290 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1291 return;
1292 }
1293
1294 #if 0
1295 /* helper lines for debugging */
1296 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1297 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1298 SelectObject(hdc, GetStockObject(DC_PEN));
1299 SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
1300 MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
1301 LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
1302 #endif
1303
1304 if (!menuBar)
1305 {
1306 HBITMAP bm;
1307 INT y = rect.top + rect.bottom;
1308 RECT rc = rect;
1309 BOOL checked = FALSE;
1310 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1311 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1312 /* Draw the check mark
1313 *
1314 * FIXME:
1315 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1316 */
1317 if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
1318 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
1319 lpitem->hbmpUnchecked;
1320 if (bm) /* we have a custom bitmap */
1321 {
1322 HDC hdcMem = CreateCompatibleDC( hdc );
1323
1324 SelectObject( hdcMem, bm );
1325 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1326 check_bitmap_width, check_bitmap_height,
1327 hdcMem, 0, 0, SRCCOPY );
1328 DeleteDC( hdcMem );
1329 checked = TRUE;
1330 }
1331 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1332 {
1333 RECT r;
1334 CopyRect(&r, &rect);
1335 r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
1336 DrawFrameControl( hdc, &r, DFC_MENU,
1337 (lpitem->fType & MFT_RADIOCHECK) ?
1338 DFCS_MENUBULLET : DFCS_MENUCHECK);
1339 checked = TRUE;
1340 }
1341 }
1342 if ( lpitem->hbmpItem )
1343 {
1344 RECT bmpRect;
1345 CopyRect(&bmpRect, &rect);
1346 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
1347 bmpRect.left += check_bitmap_width + 2;
1348 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
1349 {
1350 bmpRect.right = bmpRect.left + MenuInfo->maxBmpSize.cx;
1351 MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo->Self, WndOwner, odaction, menuBar);
1352 }
1353 }
1354 /* Draw the popup-menu arrow */
1355 if (lpitem->hSubMenu)
1356 {
1357 RECT rectTemp;
1358 CopyRect(&rectTemp, &rect);
1359 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1360 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1361 }
1362 rect.left += 4;
1363 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1364 rect.left += check_bitmap_width;
1365 rect.right -= check_bitmap_width;
1366 }
1367 else if( lpitem->hbmpItem)
1368 { /* Draw the bitmap */
1369 MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo->Self, WndOwner, odaction, menuBar);
1370 }
1371
1372 /* process text if present */
1373 if (lpitem->lpstr)
1374 {
1375 register int i = 0;
1376 HFONT hfontOld = 0;
1377
1378 UINT uFormat = menuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE
1379 : DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1380
1381 if(MenuInfo->dwStyle & MNS_CHECKORBMP)
1382 rect.left += max(0, MenuInfo->maxBmpSize.cx - GetSystemMetrics(SM_CXMENUCHECK));
1383 else
1384 rect.left += MenuInfo->maxBmpSize.cx;
1385
1386 if ( lpitem->fState & MFS_DEFAULT )
1387 {
1388 hfontOld = SelectObject(hdc, hMenuFontBold);
1389 }
1390
1391 if (menuBar) {
1392 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1393 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1394 }
1395
1396 Text = (PWCHAR) lpitem->dwTypeData;
1397 if(Text)
1398 {
1399 for (i = 0; L'\0' != Text[i]; i++)
1400 if (Text[i] == L'\t' || Text[i] == L'\b')
1401 break;
1402 }
1403
1404 if(lpitem->fState & MF_GRAYED)
1405 {
1406 if (!(lpitem->fState & MF_HILITE) )
1407 {
1408 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1409 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1410 DrawTextW( hdc, Text, i, &rect, uFormat );
1411 --rect.left; --rect.top; --rect.right; --rect.bottom;
1412 }
1413 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1414 }
1415
1416 DrawTextW( hdc, Text, i, &rect, uFormat);
1417
1418 /* paint the shortcut text */
1419 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
1420 {
1421 if (L'\t' == Text[i])
1422 {
1423 rect.left = lpitem->dxTab;
1424 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1425 }
1426 else
1427 {
1428 rect.right = lpitem->dxTab;
1429 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1430 }
1431
1432 if (lpitem->fState & MF_GRAYED)
1433 {
1434 if (!(lpitem->fState & MF_HILITE) )
1435 {
1436 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1437 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1438 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
1439 --rect.left; --rect.top; --rect.right; --rect.bottom;
1440 }
1441 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1442 }
1443 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
1444 }
1445
1446 if (hfontOld)
1447 SelectObject (hdc, hfontOld);
1448 }
1449 }
1450
1451 /***********************************************************************
1452 * MenuDrawPopupMenu
1453 *
1454 * Paint a popup menu.
1455 */
1456 static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
1457 {
1458 HBRUSH hPrevBrush = 0;
1459 RECT rect;
1460
1461 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1462
1463 GetClientRect( hwnd, &rect );
1464
1465 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1466 && (SelectObject( hdc, hMenuFont)))
1467 {
1468 HPEN hPrevPen;
1469
1470 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1471
1472 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1473 if ( hPrevPen )
1474 {
1475 BOOL flat_menu = FALSE;
1476 ROSMENUINFO MenuInfo;
1477 ROSMENUITEMINFO ItemInfo;
1478
1479 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1480 if (flat_menu)
1481 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1482 else
1483 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1484
1485 /* draw menu items */
1486 if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.MenuItemCount)
1487 {
1488 UINT u;
1489
1490 MenuInitRosMenuItemInfo(&ItemInfo);
1491
1492 for (u = 0; u < MenuInfo.MenuItemCount; u++)
1493 {
1494 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
1495 {
1496 MenuDrawMenuItem(hwnd, &MenuInfo, MenuInfo.WndOwner, hdc, &ItemInfo,
1497 MenuInfo.Height, FALSE, ODA_DRAWENTIRE);
1498 }
1499 }
1500
1501 MenuCleanupRosMenuItemInfo(&ItemInfo);
1502 }
1503 } else
1504 {
1505 SelectObject( hdc, hPrevBrush );
1506 }
1507 }
1508 }
1509
1510 /***********************************************************************
1511 * MenuDrawMenuBar
1512 *
1513 * Paint a menu bar. Returns the height of the menu bar.
1514 * called from [windows/nonclient.c]
1515 */
1516 UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1517 BOOL suppress_draw)
1518 {
1519 ROSMENUINFO lppop;
1520 HFONT hfontOld = 0;
1521 HMENU hMenu = GetMenu(hwnd);
1522
1523 if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
1524 {
1525 return GetSystemMetrics(SM_CYMENU);
1526 }
1527
1528 if (suppress_draw)
1529 {
1530 hfontOld = SelectObject(hDC, hMenuFont);
1531
1532 MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
1533
1534 lprect->bottom = lprect->top + lppop.Height;
1535
1536 if (hfontOld) SelectObject( hDC, hfontOld);
1537 return lppop.Height;
1538 }
1539 else
1540 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1541 }
1542
1543 /***********************************************************************
1544 * MenuShowPopup
1545 *
1546 * Display a popup menu.
1547 */
1548 static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1549 INT x, INT y, INT xanchor, INT yanchor )
1550 {
1551 ROSMENUINFO MenuInfo;
1552 ROSMENUITEMINFO ItemInfo;
1553 UINT width, height;
1554 POINT pt;
1555 HMONITOR monitor;
1556 MONITORINFO info;
1557 DWORD ex_style = 0;
1558
1559 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1560 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1561
1562 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
1563 if (MenuInfo.FocusedItem != NO_SELECTED_ITEM)
1564 {
1565 MenuInitRosMenuItemInfo(&ItemInfo);
1566 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
1567 {
1568 ItemInfo.fMask |= MIIM_STATE;
1569 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1570 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
1571 }
1572 MenuCleanupRosMenuItemInfo(&ItemInfo);
1573 MenuInfo.FocusedItem = NO_SELECTED_ITEM;
1574 }
1575
1576 /* store the owner for DrawItem */
1577 if (!IsWindow(hwndOwner))
1578 {
1579 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1580 return FALSE;
1581 }
1582 MenuInfo.WndOwner = hwndOwner;
1583 MenuSetRosMenuInfo(&MenuInfo);
1584
1585 MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
1586
1587 /* adjust popup menu pos so that it fits within the desktop */
1588
1589 width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
1590 height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
1591
1592 /* FIXME: should use item rect */
1593 pt.x = x;
1594 pt.y = y;
1595 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1596 info.cbSize = sizeof(info);
1597 GetMonitorInfoW( monitor, &info );
1598
1599 if (flags & TPM_LAYOUTRTL)
1600 {
1601 ex_style = WS_EX_LAYOUTRTL;
1602 flags ^= TPM_RIGHTALIGN;
1603 }
1604 if( flags & TPM_RIGHTALIGN ) x -= width;
1605 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1606
1607 if( flags & TPM_BOTTOMALIGN ) y -= height;
1608 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1609
1610 if( x + width > info.rcMonitor.right)
1611 {
1612 if( xanchor && x >= width - xanchor )
1613 x -= width - xanchor;
1614
1615 if( x + width > info.rcMonitor.right)
1616 x = info.rcMonitor.right - width;
1617 }
1618 if( x < info.rcMonitor.left ) x = info.rcMonitor.left;
1619
1620 if( y + height > info.rcMonitor.bottom)
1621 {
1622 if( yanchor && y >= height + yanchor )
1623 y -= height + yanchor;
1624
1625 if( y + height > info.rcMonitor.bottom)
1626 y = info.rcMonitor.bottom - height;
1627 }
1628 if( y < info.rcMonitor.top ) y = info.rcMonitor.top;
1629
1630 /* NOTE: In Windows, top menu popup is not owned. */
1631 MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL,
1632 WS_POPUP, x, y, width, height,
1633 hwndOwner, 0, (HINSTANCE) GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1634 (LPVOID) MenuInfo.Self);
1635 if ( !MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) return FALSE;
1636 if (!top_popup) {
1637 top_popup = MenuInfo.Wnd;
1638 top_popup_hmenu = hmenu;
1639 }
1640
1641 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
1642
1643 /* Display the window */
1644
1645 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
1646 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1647 UpdateWindow( MenuInfo.Wnd );
1648 return TRUE;
1649 }
1650
1651
1652 /***********************************************************************
1653 * MenuSelectItem
1654 */
1655 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
1656 BOOL sendMenuSelect, HMENU topmenu)
1657 {
1658 ROSMENUITEMINFO ItemInfo;
1659 ROSMENUINFO TopMenuInfo;
1660 HDC hdc;
1661
1662 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1663
1664 if (!hmenu || !hmenu->MenuItemCount || !hmenu->Wnd) return;
1665 if (hmenu->FocusedItem == wIndex) return;
1666 if (hmenu->Flags & MNF_POPUP) hdc = GetDC(hmenu->Wnd);
1667 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
1668 if (!top_popup) {
1669 top_popup = hmenu->Wnd;
1670 top_popup_hmenu = hmenu->Self;
1671 }
1672
1673 SelectObject( hdc, hMenuFont );
1674
1675 MenuInitRosMenuItemInfo(&ItemInfo);
1676
1677 /* Clear previous highlighted item */
1678 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1679 {
1680 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1681 {
1682 ItemInfo.fMask |= MIIM_STATE;
1683 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1684 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1685 }
1686 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
1687 hmenu->Height, !(hmenu->Flags & MNF_POPUP),
1688 ODA_SELECT);
1689 }
1690
1691 /* Highlight new item (if any) */
1692 hmenu->FocusedItem = wIndex;
1693 MenuSetRosMenuInfo(hmenu);
1694 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1695 {
1696 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1697 {
1698 if (!(ItemInfo.fType & MF_SEPARATOR))
1699 {
1700 ItemInfo.fMask |= MIIM_STATE;
1701 ItemInfo.fState |= MF_HILITE;
1702 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1703 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
1704 &ItemInfo, hmenu->Height, !(hmenu->Flags & MNF_POPUP),
1705 ODA_SELECT);
1706 }
1707 if (sendMenuSelect)
1708 {
1709 WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID,
1710 ItemInfo.fType | ItemInfo.fState |
1711 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
1712 (hmenu->Flags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
1713
1714 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
1715 }
1716 }
1717 }
1718 else if (sendMenuSelect)
1719 {
1720 if(topmenu)
1721 {
1722 int pos;
1723 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
1724 if (pos != NO_SELECTED_ITEM)
1725 {
1726 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
1727 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
1728 {
1729 WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
1730 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
1731 (TopMenuInfo.Flags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
1732
1733 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
1734 }
1735 }
1736 }
1737 }
1738 MenuCleanupRosMenuItemInfo(&ItemInfo);
1739 ReleaseDC(hmenu->Wnd, hdc);
1740 }
1741
1742 /***********************************************************************
1743 * MenuMoveSelection
1744 *
1745 * Moves currently selected item according to the Offset parameter.
1746 * If there is no selection then it should select the last item if
1747 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1748 */
1749 static void FASTCALL
1750 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1751 {
1752 INT i;
1753 ROSMENUITEMINFO ItemInfo;
1754 INT OrigPos;
1755
1756 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1757
1758 /* Prevent looping */
1759 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1760 return;
1761 else if (Offset < -1)
1762 Offset = -1;
1763 else if (Offset > 1)
1764 Offset = 1;
1765
1766 MenuInitRosMenuItemInfo(&ItemInfo);
1767
1768 OrigPos = MenuInfo->FocusedItem;
1769 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1770 {
1771 OrigPos = 0;
1772 i = -1;
1773 }
1774 else
1775 {
1776 i = MenuInfo->FocusedItem;
1777 }
1778
1779 do
1780 {
1781 /* Step */
1782 i += Offset;
1783 /* Clip and wrap around */
1784 if (i < 0)
1785 {
1786 i = MenuInfo->MenuItemCount - 1;
1787 }
1788 else if (i >= MenuInfo->MenuItemCount)
1789 {
1790 i = 0;
1791 }
1792 /* If this is a good candidate; */
1793 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1794 0 == (ItemInfo.fType & MF_SEPARATOR))
1795 {
1796 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1797 MenuCleanupRosMenuItemInfo(&ItemInfo);
1798 return;
1799 }
1800 } while (i != OrigPos);
1801
1802 /* Not found */
1803 MenuCleanupRosMenuItemInfo(&ItemInfo);
1804 }
1805
1806 //
1807 // This breaks some test results. Should handle A2U if called!
1808 //
1809 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1810 {
1811 #ifdef __REACTOS__
1812 PWND pWnd;
1813
1814 pWnd = ValidateHwnd(Wnd);
1815 if (pWnd)
1816 {
1817 if (!pWnd->fnid)
1818 {
1819 NtUserSetWindowFNID(Wnd, FNID_MENU);
1820 }
1821 else
1822 {
1823 if (pWnd->fnid != FNID_MENU)
1824 {
1825 ERR("Wrong window class for Menu!\n");
1826 return 0;
1827 }
1828 }
1829 }
1830 #endif
1831
1832 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1833
1834 switch(Message)
1835 {
1836 case WM_CREATE:
1837 {
1838 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1839 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1840 return 0;
1841 }
1842
1843 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1844 return MA_NOACTIVATE;
1845
1846 case WM_PAINT:
1847 {
1848 PAINTSTRUCT ps;
1849 BeginPaint(Wnd, &ps);
1850 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1851 EndPaint(Wnd, &ps);
1852 return 0;
1853 }
1854
1855 case WM_PRINTCLIENT:
1856 {
1857 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1858 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1859 return 0;
1860 }
1861
1862 case WM_ERASEBKGND:
1863 return 1;
1864
1865 case WM_DESTROY:
1866 /* zero out global pointer in case resident popup window was destroyed. */
1867 if (Wnd == top_popup)
1868 {
1869 top_popup = NULL;
1870 top_popup_hmenu = NULL;
1871 }
1872 break;
1873
1874 #ifdef __REACTOS__
1875 case WM_NCDESTROY:
1876 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
1877 break;
1878 #endif
1879
1880 case WM_SHOWWINDOW:
1881 if (0 != wParam)
1882 {
1883 if (0 == GetWindowLongPtrA(Wnd, 0))
1884 {
1885 OutputDebugStringA("no menu to display\n");
1886 }
1887 }
1888 else
1889 {
1890 SetWindowLongPtrA(Wnd, 0, 0);
1891 }
1892 break;
1893
1894 case MM_SETMENUHANDLE:
1895 SetWindowLongPtrA(Wnd, 0, wParam);
1896 break;
1897
1898 case MM_GETMENUHANDLE:
1899 case MN_GETHMENU:
1900 return GetWindowLongPtrA(Wnd, 0);
1901
1902 default:
1903 return DefWindowProcA(Wnd, Message, wParam, lParam);
1904 }
1905 return 0;
1906 }
1907
1908 LRESULT WINAPI
1909 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1910 {
1911 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
1912 PWND pWnd;
1913
1914 pWnd = ValidateHwnd(Wnd);
1915 if (pWnd)
1916 {
1917 if (!pWnd->fnid)
1918 {
1919 if (Message != WM_NCCREATE)
1920 {
1921 return DefWindowProcW(Wnd, Message, wParam, lParam);
1922 }
1923 NtUserSetWindowFNID(Wnd, FNID_MENU);
1924 }
1925 else
1926 {
1927 if (pWnd->fnid != FNID_MENU)
1928 {
1929 ERR("Wrong window class for Menu!\n");
1930 return 0;
1931 }
1932 }
1933 }
1934 #endif
1935
1936 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1937
1938 switch(Message)
1939 {
1940 case WM_CREATE:
1941 {
1942 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1943 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1944 return 0;
1945 }
1946
1947 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1948 return MA_NOACTIVATE;
1949
1950 case WM_PAINT:
1951 {
1952 PAINTSTRUCT ps;
1953 BeginPaint(Wnd, &ps);
1954 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1955 EndPaint(Wnd, &ps);
1956 return 0;
1957 }
1958
1959 case WM_PRINTCLIENT:
1960 {
1961 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1962 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1963 return 0;
1964 }
1965
1966 case WM_ERASEBKGND:
1967 return 1;
1968
1969 case WM_DESTROY:
1970 /* zero out global pointer in case resident popup window was destroyed. */
1971 if (Wnd == top_popup)
1972 {
1973 top_popup = NULL;
1974 top_popup_hmenu = NULL;
1975 }
1976 break;
1977
1978 case WM_SHOWWINDOW:
1979 if (0 != wParam)
1980 {
1981 if (0 == GetWindowLongPtrW(Wnd, 0))
1982 {
1983 OutputDebugStringA("no menu to display\n");
1984 }
1985 }
1986 else
1987 {
1988 SetWindowLongPtrW(Wnd, 0, 0);
1989 }
1990 break;
1991
1992 case MM_SETMENUHANDLE:
1993 SetWindowLongPtrW(Wnd, 0, wParam);
1994 break;
1995
1996 case MM_GETMENUHANDLE:
1997 case MN_GETHMENU:
1998 return GetWindowLongPtrW(Wnd, 0);
1999
2000 default:
2001 return DefWindowProcW(Wnd, Message, wParam, lParam);
2002 }
2003
2004 return 0;
2005 }
2006
2007 /**********************************************************************
2008 * MENU_ParseResource
2009 *
2010 * Parse a standard menu resource and add items to the menu.
2011 * Return a pointer to the end of the resource.
2012 *
2013 * NOTE: flags is equivalent to the mtOption field
2014 */
2015 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
2016 {
2017 WORD flags, id = 0;
2018 HMENU hSubMenu;
2019 LPCWSTR str;
2020 BOOL end = FALSE;
2021
2022 do
2023 {
2024 flags = GET_WORD(res);
2025
2026 /* remove MF_END flag before passing it to AppendMenu()! */
2027 end = (flags & MF_END);
2028 if(end) flags ^= MF_END;
2029
2030 res += sizeof(WORD);
2031 if(!(flags & MF_POPUP))
2032 {
2033 id = GET_WORD(res);
2034 res += sizeof(WORD);
2035 }
2036 str = (LPCWSTR)res;
2037 res += (strlenW(str) + 1) * sizeof(WCHAR);
2038
2039 if (flags & MF_POPUP)
2040 {
2041 hSubMenu = CreatePopupMenu();
2042 if(!hSubMenu) return NULL;
2043 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
2044 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
2045 }
2046 else /* Not a popup */
2047 {
2048 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2049 }
2050 } while(!end);
2051 return res;
2052 }
2053
2054
2055 /**********************************************************************
2056 * MENUEX_ParseResource
2057 *
2058 * Parse an extended menu resource and add items to the menu.
2059 * Return a pointer to the end of the resource.
2060 */
2061 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2062 {
2063 WORD resinfo;
2064 do
2065 {
2066 MENUITEMINFOW mii;
2067
2068 mii.cbSize = sizeof(mii);
2069 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2070 mii.fType = GET_DWORD(res);
2071 res += sizeof(DWORD);
2072 mii.fState = GET_DWORD(res);
2073 res += sizeof(DWORD);
2074 mii.wID = GET_DWORD(res);
2075 res += sizeof(DWORD);
2076 resinfo = GET_WORD(res);
2077 res += sizeof(WORD);
2078 /* Align the text on a word boundary. */
2079 res += (~((UINT_PTR)res - 1)) & 1;
2080 mii.dwTypeData = (LPWSTR)res;
2081 mii.cch = strlenW(mii.dwTypeData);
2082 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2083 /* Align the following fields on a dword boundary. */
2084 res += (~((UINT_PTR)res - 1)) & 3;
2085
2086 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2087 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2088
2089 if (resinfo & 1) /* Pop-up? */
2090 {
2091 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2092 res += sizeof(DWORD);
2093 mii.hSubMenu = CreatePopupMenu();
2094 if (!mii.hSubMenu)
2095 {
2096 ERR("CreatePopupMenu failed\n");
2097 return NULL;
2098 }
2099
2100 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2101 {
2102 ERR("MENUEX_ParseResource failed\n");
2103 DestroyMenu(mii.hSubMenu);
2104 return NULL;
2105 }
2106 mii.fMask |= MIIM_SUBMENU;
2107 }
2108 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
2109 {
2110 mii.fType |= MF_SEPARATOR;
2111 }
2112 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2113 } while (!(resinfo & MF_END));
2114 return res;
2115 }
2116
2117 NTSTATUS WINAPI
2118 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2119 {
2120 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2121 LRESULT Result = (LRESULT)hmenu;
2122 MENUINFO menuinfo = {0};
2123 MENUITEMINFOW info = {0};
2124
2125 // removing space for checkboxes from menu
2126 menuinfo.cbSize = sizeof(menuinfo);
2127 menuinfo.fMask = MIM_STYLE;
2128 GetMenuInfo(hmenu, &menuinfo);
2129 menuinfo.dwStyle |= MNS_NOCHECK;
2130 SetMenuInfo(hmenu, &menuinfo);
2131
2132 // adding bitmaps to menu items
2133 info.cbSize = sizeof(info);
2134 info.fMask |= MIIM_BITMAP;
2135 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2136 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2137 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2138 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2139 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2140 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2141 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2142 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2143
2144 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2145 }
2146
2147
2148 BOOL
2149 MenuInit(VOID)
2150 {
2151 NONCLIENTMETRICSW ncm;
2152
2153 /* get the menu font */
2154 if(!hMenuFont || !hMenuFontBold)
2155 {
2156 ncm.cbSize = sizeof(ncm);
2157 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2158 {
2159 ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2160 return FALSE;
2161 }
2162
2163 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2164 if(hMenuFont == NULL)
2165 {
2166 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2167 return FALSE;
2168 }
2169
2170 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2171 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2172 if(hMenuFontBold == NULL)
2173 {
2174 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2175 DeleteObject(hMenuFont);
2176 hMenuFont = NULL;
2177 return FALSE;
2178 }
2179 }
2180
2181 return TRUE;
2182 }
2183
2184 VOID
2185 MenuCleanup(VOID)
2186 {
2187 if (hMenuFont)
2188 {
2189 DeleteObject(hMenuFont);
2190 hMenuFont = NULL;
2191 }
2192
2193 if (hMenuFontBold)
2194 {
2195 DeleteObject(hMenuFontBold);
2196 hMenuFontBold = NULL;
2197 }
2198 }
2199
2200 /***********************************************************************
2201 * DrawMenuBarTemp (USER32.@)
2202 *
2203 * UNDOCUMENTED !!
2204 *
2205 * called by W98SE desk.cpl Control Panel Applet
2206 *
2207 * Not 100% sure about the param names, but close.
2208 *
2209 * @implemented
2210 */
2211 DWORD WINAPI
2212 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2213 {
2214 ROSMENUINFO MenuInfo;
2215 ROSMENUITEMINFO ItemInfo;
2216 UINT i;
2217 HFONT FontOld = NULL;
2218 BOOL flat_menu = FALSE;
2219
2220 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2221
2222 if (NULL == Menu)
2223 {
2224 Menu = GetMenu(Wnd);
2225 }
2226
2227 if (NULL == Font)
2228 {
2229 Font = hMenuFont;
2230 }
2231
2232 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2233 {
2234 return GetSystemMetrics(SM_CYMENU);
2235 }
2236
2237 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2238
2239 FontOld = SelectObject(DC, Font);
2240
2241 if (0 == MenuInfo.Height)
2242 {
2243 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2244 }
2245
2246 Rect->bottom = Rect->top + MenuInfo.Height;
2247
2248 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2249
2250 SelectObject(DC, GetStockObject(DC_PEN));
2251 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2252 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2253 LineTo(DC, Rect->right, Rect->bottom - 1);
2254
2255 if (0 == MenuInfo.MenuItemCount)
2256 {
2257 SelectObject(DC, FontOld);
2258 return GetSystemMetrics(SM_CYMENU);
2259 }
2260
2261 MenuInitRosMenuItemInfo(&ItemInfo);
2262 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2263 {
2264 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2265 {
2266 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2267 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2268 }
2269 }
2270 MenuCleanupRosMenuItemInfo(&ItemInfo);
2271
2272 SelectObject(DC, FontOld);
2273
2274 return MenuInfo.Height;
2275 }
2276
2277 /***********************************************************************
2278 * MenuShowSubPopup
2279 *
2280 * Display the sub-menu of the selected item of this menu.
2281 * Return the handle of the submenu, or menu if no submenu to display.
2282 */
2283 static HMENU FASTCALL
2284 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2285 {
2286 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2287 RECT Rect;
2288 ROSMENUITEMINFO ItemInfo;
2289 ROSMENUINFO SubMenuInfo;
2290 HDC Dc;
2291 HMENU Ret;
2292
2293 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2294
2295 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2296 {
2297 return MenuInfo->Self;
2298 }
2299
2300 MenuInitRosMenuItemInfo(&ItemInfo);
2301 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2302 {
2303 MenuCleanupRosMenuItemInfo(&ItemInfo);
2304 return MenuInfo->Self;
2305 }
2306 if (0 == (ItemInfo.hSubMenu) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2307 {
2308 MenuCleanupRosMenuItemInfo(&ItemInfo);
2309 return MenuInfo->Self;
2310 }
2311
2312 /* message must be sent before using item,
2313 because nearly everything may be changed by the application ! */
2314
2315 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2316 if (0 == (Flags & TPM_NONOTIFY))
2317 {
2318 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2319 MAKELPARAM(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2320 }
2321
2322 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2323 {
2324 MenuCleanupRosMenuItemInfo(&ItemInfo);
2325 return MenuInfo->Self;
2326 }
2327 Rect = ItemInfo.Rect;
2328
2329 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2330 if (0 == (ItemInfo.fState & MF_HILITE))
2331 {
2332 if (0 != (MenuInfo->Flags & MNF_POPUP))
2333 {
2334 Dc = GetDC(MenuInfo->Wnd);
2335 }
2336 else
2337 {
2338 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2339 }
2340
2341 SelectObject(Dc, hMenuFont);
2342 ItemInfo.fMask |= MIIM_STATE;
2343 ItemInfo.fState |= MF_HILITE;
2344 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2345 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2346 ! (MenuInfo->Flags & MNF_POPUP), ODA_DRAWENTIRE);
2347 ReleaseDC(MenuInfo->Wnd, Dc);
2348 }
2349
2350 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2351 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2352 {
2353 ItemInfo.Rect = Rect;
2354 }
2355
2356 ItemInfo.fMask |= MIIM_STATE;
2357 ItemInfo.fState |= MF_MOUSESELECT;
2358 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2359
2360 if (IS_SYSTEM_MENU(MenuInfo))
2361 {
2362 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2363 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2364 if (Flags & TPM_LAYOUTRTL) Rect.left;
2365 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2366 Rect.top = Rect.bottom;
2367 Rect.right = GetSystemMetrics(SM_CXSIZE);
2368 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2369 }
2370 else
2371 {
2372 GetWindowRect(MenuInfo->Wnd, &Rect);
2373 if (0 != (MenuInfo->Flags & MNF_POPUP))
2374 {
2375 if(Flags & TPM_LAYOUTRTL)
2376 Rect.left += GetSystemMetrics(SM_CXBORDER);
2377 else
2378 Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER);
2379 Rect.top += ItemInfo.Rect.top - MENU_TOP_MARGIN;//3;
2380 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2381 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
2382 - GetSystemMetrics(SM_CYBORDER);
2383 }
2384 else
2385 {
2386 if(Flags & TPM_LAYOUTRTL)
2387 Rect.left += Rect.right - ItemInfo.Rect.left;
2388 else
2389 Rect.left += ItemInfo.Rect.left;
2390 Rect.top += ItemInfo.Rect.bottom;
2391 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2392 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2393 }
2394 }
2395
2396 /* use default alignment for submenus */
2397 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2398
2399 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2400 Rect.left, Rect.top, Rect.right, Rect.bottom );
2401 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2402 {
2403 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2404 }
2405
2406 Ret = ItemInfo.hSubMenu;
2407 MenuCleanupRosMenuItemInfo(&ItemInfo);
2408
2409 return Ret;
2410 }
2411
2412 /**********************************************************************
2413 * MENU_EndMenu
2414 *
2415 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2416 *
2417 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2418 */
2419 void MENU_EndMenu( HWND hwnd )
2420 {
2421 ROSMENUINFO MenuInfo;
2422 BOOL Ret = FALSE;
2423 if (top_popup_hmenu)
2424 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2425 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2426 }
2427
2428 /***********************************************************************
2429 * MenuHideSubPopups
2430 *
2431 * Hide the sub-popup menus of this menu.
2432 */
2433 static void FASTCALL
2434 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2435 BOOL SendMenuSelect, UINT wFlags)
2436 {
2437 ROSMENUINFO SubMenuInfo;
2438 ROSMENUITEMINFO ItemInfo;
2439
2440 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2441
2442 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2443 {
2444 MenuInitRosMenuItemInfo(&ItemInfo);
2445 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2446 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2447 || 0 == (ItemInfo.hSubMenu)
2448 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2449 {
2450 MenuCleanupRosMenuItemInfo(&ItemInfo);
2451 return;
2452 }
2453 ItemInfo.fState &= ~MF_MOUSESELECT;
2454 ItemInfo.fMask |= MIIM_STATE;
2455 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2456 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2457 {
2458 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2459 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2460 DestroyWindow(SubMenuInfo.Wnd);
2461 SubMenuInfo.Wnd = NULL;
2462 MenuSetRosMenuInfo(&SubMenuInfo);
2463
2464 if (!(wFlags & TPM_NONOTIFY))
2465 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2466 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2467 }
2468 }
2469 }
2470
2471 /***********************************************************************
2472 * MenuSwitchTracking
2473 *
2474 * Helper function for menu navigation routines.
2475 */
2476 static void FASTCALL
2477 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2478 {
2479 ROSMENUINFO TopMenuInfo;
2480
2481 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2482
2483 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2484 Mt->TopMenu != PtMenuInfo->Self &&
2485 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MNF_POPUP))
2486 {
2487 /* both are top level menus (system and menu-bar) */
2488 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2489 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2490 Mt->TopMenu = PtMenuInfo->Self;
2491 }
2492 else
2493 {
2494 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2495 }
2496
2497 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2498 }
2499
2500 /***********************************************************************
2501 * MenuExecFocusedItem
2502 *
2503 * Execute a menu item (for instance when user pressed Enter).
2504 * Return the wID of the executed item. Otherwise, -1 indicating
2505 * that no menu item was executed, -2 if a popup is shown;
2506 * Have to receive the flags for the TrackPopupMenu options to avoid
2507 * sending unwanted message.
2508 *
2509 */
2510 static INT FASTCALL
2511 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2512 {
2513 ROSMENUITEMINFO ItemInfo;
2514 UINT wID;
2515
2516 TRACE("%p menu=%p\n", Mt, MenuInfo);
2517
2518 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2519 {
2520 return -1;
2521 }
2522
2523 MenuInitRosMenuItemInfo(&ItemInfo);
2524 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2525 {
2526 MenuCleanupRosMenuItemInfo(&ItemInfo);
2527 return -1;
2528 }
2529
2530 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2531
2532 if (0 == (ItemInfo.hSubMenu))
2533 {
2534 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2535 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2536 {
2537 /* If TPM_RETURNCMD is set you return the id, but
2538 do not send a message to the owner */
2539 if (0 == (Flags & TPM_RETURNCMD))
2540 {
2541 if (0 != (MenuInfo->Flags & MNF_SYSDESKMN))
2542 {
2543 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2544 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2545 }
2546 else
2547 {
2548 ROSMENUINFO topmenuI;
2549 BOOL ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu);
2550 DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
2551
2552 if (dwStyle & MNS_NOTIFYBYPOS)
2553 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->FocusedItem, (LPARAM)MenuInfo->Self);
2554 else
2555 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2556 }
2557 }
2558 wID = ItemInfo.wID;
2559 MenuCleanupRosMenuItemInfo(&ItemInfo);
2560 return wID;
2561 }
2562 }
2563 else
2564 {
2565 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2566 return -2;
2567 }
2568
2569 return -1;
2570 }
2571
2572 /***********************************************************************
2573 * MenuButtonDown
2574 *
2575 * Return TRUE if we can go on with menu tracking.
2576 */
2577 static BOOL FASTCALL
2578 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2579 {
2580 int Index;
2581 ROSMENUINFO MenuInfo;
2582 ROSMENUITEMINFO Item;
2583
2584 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2585
2586 if (NULL != PtMenu)
2587 {
2588 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2589 {
2590 return FALSE;
2591 }
2592 if (IS_SYSTEM_MENU(&MenuInfo))
2593 {
2594 Index = 0;
2595 }
2596 else
2597 {
2598 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2599 }
2600 MenuInitRosMenuItemInfo(&Item);
2601 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2602 {
2603 MenuCleanupRosMenuItemInfo(&Item);
2604 return FALSE;
2605 }
2606
2607 if (!(Item.fType & MF_SEPARATOR) &&
2608 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2609 {
2610 if (MenuInfo.FocusedItem != Index)
2611 {
2612 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2613 }
2614
2615 /* If the popup menu is not already "popped" */
2616 if (0 == (Item.fState & MF_MOUSESELECT))
2617 {
2618 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2619 }
2620 }
2621
2622 MenuCleanupRosMenuItemInfo(&Item);
2623
2624 return TRUE;
2625 }
2626
2627 /* else the click was on the menu bar, finish the tracking */
2628
2629 return FALSE;
2630 }
2631
2632 /***********************************************************************
2633 * MenuButtonUp
2634 *
2635 * Return the value of MenuExecFocusedItem if
2636 * the selected item was not a popup. Else open the popup.
2637 * A -1 return value indicates that we go on with menu tracking.
2638 *
2639 */
2640 static INT FASTCALL
2641 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2642 {
2643 INT Id;
2644 ROSMENUINFO MenuInfo;
2645 ROSMENUITEMINFO ItemInfo;
2646
2647 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2648
2649 if (NULL != PtMenu)
2650 {
2651 Id = 0;
2652 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2653 {
2654 return -1;
2655 }
2656
2657 if (! IS_SYSTEM_MENU(&MenuInfo))
2658 {
2659 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2660 }
2661 MenuInitRosMenuItemInfo(&ItemInfo);
2662 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2663 MenuInfo.FocusedItem == Id)
2664 {
2665 if (0 == (ItemInfo.hSubMenu))
2666 {
2667 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2668 MenuCleanupRosMenuItemInfo(&ItemInfo);
2669 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2670 }
2671 MenuCleanupRosMenuItemInfo(&ItemInfo);
2672
2673 /* If we are dealing with the top-level menu */
2674 /* and this is a click on an already "popped" item: */
2675 /* Stop the menu tracking and close the opened submenus */
2676 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2677 {
2678 MenuCleanupRosMenuItemInfo(&ItemInfo);
2679 return 0;
2680 }
2681 }
2682 MenuCleanupRosMenuItemInfo(&ItemInfo);
2683 MenuInfo.TimeToHide = TRUE;
2684 MenuSetRosMenuInfo(&MenuInfo);
2685 }
2686
2687 return -1;
2688 }
2689
2690 /***********************************************************************
2691 * MenuPtMenu
2692 *
2693 * Walks menu chain trying to find a menu pt maps to.
2694 */
2695 static HMENU FASTCALL
2696 MenuPtMenu(HMENU Menu, POINT Pt)
2697 {
2698 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2699 ROSMENUINFO MenuInfo;
2700 ROSMENUITEMINFO ItemInfo;
2701 HMENU Ret = NULL;
2702 INT Ht;
2703
2704 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2705 {
2706 return NULL;
2707 }
2708
2709 /* try subpopup first (if any) */
2710 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2711 {
2712 MenuInitRosMenuItemInfo(&ItemInfo);
2713 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2714 0 != (ItemInfo.hSubMenu) &&
2715 0 != (ItemInfo.fState & MF_MOUSESELECT))
2716 {
2717 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2718 if (NULL != Ret)
2719 {
2720 MenuCleanupRosMenuItemInfo(&ItemInfo);
2721 return Ret;
2722 }
2723 }
2724 MenuCleanupRosMenuItemInfo(&ItemInfo);
2725 }
2726
2727 /* check the current window (avoiding WM_HITTEST) */
2728 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2729 if (0 != (MenuInfo.Flags & MNF_POPUP))
2730 {
2731 if (HTNOWHERE != Ht && HTERROR != Ht)
2732 {
2733 Ret = Menu;
2734 }
2735 }
2736 else if (HTSYSMENU == Ht)
2737 {
2738 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2739 }
2740 else if (HTMENU == Ht)
2741 {
2742 Ret = GetMenu(MenuInfo.Wnd);
2743 }
2744
2745 return Ret;
2746 }
2747
2748 /***********************************************************************
2749 * MenuMouseMove
2750 *
2751 * Return TRUE if we can go on with menu tracking.
2752 */
2753 static BOOL FASTCALL
2754 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2755 {
2756 INT Index;
2757 ROSMENUINFO MenuInfo;
2758 ROSMENUITEMINFO ItemInfo;
2759
2760 if (NULL != PtMenu)
2761 {
2762 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2763 {
2764 return TRUE;
2765 }
2766 if (IS_SYSTEM_MENU(&MenuInfo))
2767 {
2768 Index = 0;
2769 }
2770 else
2771 {
2772 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2773 }
2774 }
2775 else
2776 {
2777 Index = NO_SELECTED_ITEM;
2778 }
2779
2780 if (NO_SELECTED_ITEM == Index)
2781 {
2782 if (Mt->CurrentMenu == MenuInfo.Self ||
2783 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2784 {
2785 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2786 TRUE, Mt->TopMenu);
2787 }
2788 }
2789 else if (MenuInfo.FocusedItem != Index)
2790 {
2791 MenuInitRosMenuItemInfo(&ItemInfo);
2792 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2793 !(ItemInfo.fType & MF_SEPARATOR))
2794 {
2795 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2796 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2797 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2798 }
2799 MenuCleanupRosMenuItemInfo(&ItemInfo);
2800 }
2801
2802 return TRUE;
2803 }
2804
2805 /***********************************************************************
2806 * MenuGetSubPopup
2807 *
2808 * Return the handle of the selected sub-popup menu (if any).
2809 */
2810 static HMENU FASTCALL
2811 MenuGetSubPopup(HMENU Menu)
2812 {
2813 ROSMENUINFO MenuInfo;
2814 ROSMENUITEMINFO ItemInfo;
2815
2816 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2817 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2818 {
2819 return NULL;
2820 }
2821
2822 MenuInitRosMenuItemInfo(&ItemInfo);
2823 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2824 {
2825 MenuCleanupRosMenuItemInfo(&ItemInfo);
2826 return NULL;
2827 }
2828 if (0 != (ItemInfo.hSubMenu) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2829 {
2830 MenuCleanupRosMenuItemInfo(&ItemInfo);
2831 return ItemInfo.hSubMenu;
2832 }
2833
2834 MenuCleanupRosMenuItemInfo(&ItemInfo);
2835 return NULL;
2836 }
2837
2838 /***********************************************************************
2839 * MenuDoNextMenu
2840 *
2841 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2842 */
2843 static LRESULT FASTCALL
2844 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2845 {
2846 ROSMENUINFO TopMenuInfo;
2847 ROSMENUINFO MenuInfo;
2848
2849 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2850 {
2851 return (LRESULT) FALSE;
2852 }
2853
2854 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2855 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2856 {
2857 MDINEXTMENU NextMenu;
2858 HMENU NewMenu;
2859 HWND NewWnd;
2860 UINT Id = 0;
2861
2862 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2863 NextMenu.hmenuNext = NULL;
2864 NextMenu.hwndNext = NULL;
2865 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2866
2867 TRACE("%p [%p] -> %p [%p]\n",
2868 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2869
2870 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2871 {
2872 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2873 NewWnd = Mt->OwnerWnd;
2874 if (IS_SYSTEM_MENU(&TopMenuInfo))
2875 {
2876 /* switch to the menu bar */
2877
2878 if (0 != (Style & WS_CHILD)
2879 || NULL == (NewMenu = GetMenu(NewWnd)))
2880 {
2881 return FALSE;
2882 }
2883
2884 if (VK_LEFT == Vk)
2885 {
2886 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2887 {
2888 return FALSE;
2889 }
2890 Id = MenuInfo.MenuItemCount - 1;
2891 }
2892 }
2893 else if (0 != (Style & WS_SYSMENU))
2894 {
2895 /* switch to the system menu */
2896 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2897 }
2898 else
2899 {
2900 return FALSE;
2901 }
2902 }
2903 else /* application returned a new menu to switch to */
2904 {
2905 NewMenu = NextMenu.hmenuNext;
2906 NewWnd = NextMenu.hwndNext;
2907
2908 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2909 {
2910 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2911
2912 if (0 != (Style & WS_SYSMENU)
2913 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2914 {
2915 /* get the real system menu */
2916 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2917 }
2918 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2919 {
2920 /* FIXME: Not sure what to do here;
2921 * perhaps try to track NewMenu as a popup? */
2922
2923 WARN(" -- got confused.\n");
2924 return FALSE;
2925 }
2926 }
2927 else
2928 {
2929 return FALSE;
2930 }
2931 }
2932
2933 if (NewMenu != Mt->TopMenu)
2934 {
2935 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2936 FALSE, 0 );
2937 if (Mt->CurrentMenu != Mt->TopMenu)
2938 {
2939 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2940 }
2941 }
2942
2943 if (NewWnd != Mt->OwnerWnd)
2944 {
2945 Mt->OwnerWnd = NewWnd;
2946 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
2947 SetCapture(Mt->OwnerWnd); // 2
2948 }
2949
2950 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2951 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2952 {
2953 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2954 }
2955
2956 return TRUE;
2957 }
2958
2959 return FALSE;
2960 }
2961
2962 /***********************************************************************
2963 * MenuSuspendPopup
2964 *
2965 * The idea is not to show the popup if the next input message is
2966 * going to hide it anyway.
2967 */
2968 static BOOL FASTCALL
2969 MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
2970 {
2971 MSG msg;
2972
2973 msg.hwnd = Mt->OwnerWnd;
2974
2975 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
2976 // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
2977
2978 switch( uMsg )
2979 {
2980 case WM_KEYDOWN:
2981 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2982 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2983 {
2984 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2985 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2986 if( msg.message == WM_KEYDOWN &&
2987 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2988 {
2989 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2990 return TRUE;
2991 }
2992 }
2993 break;
2994 }
2995 /* failures go through this */
2996 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2997 return FALSE;
2998 }
2999
3000 /***********************************************************************
3001 * MenuKeyEscape
3002 *
3003 * Handle a VK_ESCAPE key event in a menu.
3004 */
3005 static BOOL FASTCALL
3006 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3007 {
3008 BOOL EndMenu = TRUE;
3009 ROSMENUINFO MenuInfo;
3010 HMENU MenuTmp, MenuPrev;
3011
3012 if (Mt->CurrentMenu != Mt->TopMenu)
3013 {
3014 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3015 && 0 != (MenuInfo.Flags & MNF_POPUP))
3016 {
3017 MenuPrev = MenuTmp = Mt->TopMenu;
3018
3019 /* close topmost popup */
3020 while (MenuTmp != Mt->CurrentMenu)
3021 {
3022 MenuPrev = MenuTmp;
3023 MenuTmp = MenuGetSubPopup(MenuPrev);
3024 }
3025
3026 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3027 {
3028 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
3029 }
3030 Mt->CurrentMenu = MenuPrev;
3031 EndMenu = FALSE;
3032 }
3033 }
3034
3035 return EndMenu;
3036 }
3037
3038 /***********************************************************************
3039 * MenuKeyLeft
3040 *
3041 * Handle a VK_LEFT key event in a menu.
3042 */
3043 static void FASTCALL
3044 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3045 {
3046 ROSMENUINFO MenuInfo;
3047 ROSMENUINFO TopMenuInfo;
3048 ROSMENUINFO PrevMenuInfo;
3049 HMENU MenuTmp, MenuPrev;
3050 UINT PrevCol;
3051
3052 MenuPrev = MenuTmp = Mt->TopMenu;
3053
3054 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3055 {
3056 return;
3057 }
3058
3059 /* Try to move 1 column left (if possible) */
3060 if ( (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)) != NO_SELECTED_ITEM)
3061 {
3062 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3063 {
3064 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3065 }
3066 return;
3067 }
3068
3069 /* close topmost popup */
3070 while (MenuTmp != Mt->CurrentMenu)
3071 {
3072 MenuPrev = MenuTmp;
3073 MenuTmp = MenuGetSubPopup(MenuPrev);
3074 }
3075
3076 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3077 {
3078 return;
3079 }
3080 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3081 Mt->CurrentMenu = MenuPrev;
3082
3083 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3084 {
3085 return;
3086 }
3087 if ((MenuPrev == Mt->TopMenu) && !(TopMenuInfo.Flags & MNF_POPUP))
3088 {
3089 /* move menu bar selection if no more popups are left */
3090
3091 if (!MenuDoNextMenu(Mt, VK_LEFT, Flags))
3092 {
3093 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3094 }
3095
3096 if (MenuPrev != MenuTmp || Mt->TrackFlags & TF_SUSPENDPOPUP)
3097 {
3098 /* A sublevel menu was displayed - display the next one
3099 * unless there is another displacement coming up */
3100
3101 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3102 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3103 {
3104 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3105 TRUE, Flags);
3106 }
3107 }
3108 }
3109 }
3110
3111 /***********************************************************************
3112 * MenuKeyRight
3113 *
3114 * Handle a VK_RIGHT key event in a menu.
3115 */
3116 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3117 {
3118 HMENU hmenutmp;
3119 ROSMENUINFO MenuInfo;
3120 ROSMENUINFO CurrentMenuInfo;
3121 UINT NextCol;
3122
3123 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3124 Mt->CurrentMenu, Mt->TopMenu);
3125
3126 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3127 if ((MenuInfo.Flags & MNF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3128 {
3129 /* If already displaying a popup, try to display sub-popup */
3130
3131 hmenutmp = Mt->CurrentMenu;
3132 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3133 {
3134 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3135 }
3136
3137 /* if subpopup was displayed then we are done */
3138 if (hmenutmp != Mt->CurrentMenu) return;
3139 }
3140
3141 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3142 {
3143 return;
3144 }
3145
3146 /* Check to see if there's another column */
3147 if ( (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)) != NO_SELECTED_ITEM)
3148 {
3149 TRACE("Going to %d.\n", NextCol);
3150 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3151 {
3152 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3153 }
3154 return;
3155 }
3156
3157 if (!(MenuInfo.Flags & MNF_POPUP)) /* menu bar tracking */
3158 {
3159 if (Mt->CurrentMenu != Mt->TopMenu)
3160 {
3161 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3162 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3163 }
3164 else
3165 {
3166 hmenutmp = NULL;
3167 }
3168
3169 /* try to move to the next item */
3170 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3171 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3172
3173 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3174 {
3175 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3176 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3177 {
3178 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3179 TRUE, Flags);
3180 }
3181 }
3182 }
3183 }
3184
3185 /***********************************************************************
3186 * MenuTrackMenu
3187 *
3188 * Menu tracking code.
3189 */
3190 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3191 HWND hwnd, const RECT *lprect )
3192 {
3193 MSG msg;
3194 ROSMENUINFO MenuInfo;
3195 ROSMENUITEMINFO ItemInfo;
3196 BOOL fRemove;
3197 INT executedMenuId = -1;
3198 MTRACKER mt;
3199 HWND capture_win;
3200 BOOL enterIdleSent = FALSE;
3201
3202 mt.TrackFlags = 0;
3203 mt.CurrentMenu = hmenu;
3204 mt.TopMenu = hmenu;
3205 mt.OwnerWnd = hwnd;
3206 mt.Pt.x = x;
3207 mt.Pt.y = y;
3208
3209 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3210 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3211 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3212
3213 if (!IsMenu(hmenu))
3214 {
3215 WARN("Invalid menu handle %p\n", hmenu);
3216 SetLastError( ERROR_INVALID_MENU_HANDLE );
3217 return FALSE;
3218 }
3219
3220 fEndMenu = FALSE;
3221 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3222 {
3223 return FALSE;
3224 }
3225
3226 if (wFlags & TPM_BUTTONDOWN)
3227 {
3228 /* Get the result in order to start the tracking or not */
3229 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3230 fEndMenu = !fRemove;
3231 }
3232
3233 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3234
3235 /* owner may not be visible when tracking a popup, so use the menu itself */
3236 capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd;
3237 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1
3238 SetCapture(capture_win); // 2
3239
3240 while (! fEndMenu)
3241 {
3242 BOOL ErrorExit = FALSE;
3243 PVOID menu = ValidateHandle(mt.CurrentMenu, TYPE_MENU);
3244 if (!menu) /* sometimes happens if I do a window manager close */
3245 break;
3246
3247 /* we have to keep the message in the queue until it's
3248 * clear that menu loop is not over yet. */
3249
3250 for (;;)
3251 {
3252 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3253 {
3254 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3255 /* remove the message from the queue */
3256 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3257 }
3258 else
3259 {
3260 /* ReactOS Check */
3261 if (!ValidateHwnd(mt.OwnerWnd) || !ValidateHwnd(MenuInfo.Wnd))
3262 {
3263 ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works.
3264 break;
3265 }
3266 if (!enterIdleSent)
3267 {
3268 HWND win = MenuInfo.Flags & MNF_POPUP ? MenuInfo.Wnd : NULL;
3269 enterIdleSent = TRUE;
3270 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3271 }
3272 WaitMessage();
3273 }
3274 }
3275
3276 if (ErrorExit) break; // Gracefully dropout.
3277
3278 /* check if EndMenu() tried to cancel us, by posting this message */
3279 if (msg.message == WM_CANCELMODE)
3280 {
3281 /* we are now out of the loop */
3282 fEndMenu = TRUE;
3283
3284 /* remove the message from the queue */
3285 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3286
3287 /* break out of internal loop, ala ESCAPE */
3288 break;
3289 }
3290
3291 TranslateMessage( &msg );
3292 mt.Pt = msg.pt;
3293
3294 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3295 enterIdleSent=FALSE;
3296
3297 fRemove = FALSE;
3298 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3299 {
3300 /*
3301 * Use the mouse coordinates in lParam instead of those in the MSG
3302 * struct to properly handle synthetic messages. They are already
3303 * in screen coordinates.
3304 */
3305 mt.Pt.x = (short)LOWORD(msg.lParam);
3306 mt.Pt.y = (short)HIWORD(msg.lParam);
3307
3308 /* Find a menu for this mouse event */
3309 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
3310
3311 switch(msg.message)
3312 {
3313 /* no WM_NC... messages in captured state */
3314
3315 case WM_RBUTTONDBLCLK:
3316 case WM_RBUTTONDOWN:
3317 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3318 /* fall through */
3319 case WM_LBUTTONDBLCLK:
3320 case WM_LBUTTONDOWN:
3321 /* If the message belongs to the menu, removes it from the queue */
3322 /* Else, end menu tracking */
3323 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
3324 fEndMenu = !fRemove;
3325 break;
3326
3327 case WM_RBUTTONUP:
3328 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3329 /* fall through */
3330 case WM_LBUTTONUP:
3331 /* Check if a menu was selected by the mouse */
3332 if (hmenu)
3333 {
3334 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
3335 TRACE("executedMenuId %d\n", executedMenuId);
3336
3337 /* End the loop if executedMenuId is an item ID */
3338 /* or if the job was done (executedMenuId = 0). */
3339 fEndMenu = fRemove = (executedMenuId != -1);
3340 }
3341 /* No menu was selected by the mouse */
3342 /* if the function was called by TrackPopupMenu, continue
3343 with the menu tracking. If not, stop it */
3344 else
3345 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3346
3347 break;
3348
3349 case WM_MOUSEMOVE:
3350 /* the selected menu item must be changed every time */
3351 /* the mouse moves. */
3352
3353 if (hmenu)
3354 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3355
3356 } /* switch(msg.message) - mouse */
3357 }
3358 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3359 {
3360 fRemove = TRUE; /* Keyboard messages are always removed */
3361 switch(msg.message)
3362 {
3363 case WM_KEYDOWN:
3364 case WM_SYSKEYDOWN:
3365 switch(msg.wParam)
3366 {
3367 case VK_MENU:
3368 case VK_F10:
3369 fEndMenu = TRUE;
3370 break;
3371
3372 case VK_HOME:
3373 case VK_END:
3374 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3375 {
3376 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
3377 NO_SELECTED_ITEM, FALSE, 0 );
3378 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3379 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3380 }
3381 break;
3382
3383 case VK_UP:
3384 case VK_DOWN: /* If on menu bar, pull-down the menu */
3385 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3386 {
3387 if (!(MenuInfo.Flags & MNF_POPUP))
3388 {
3389 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3390 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3391 }
3392 else /* otherwise try to move selection */
3393 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3394 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3395 }
3396 break;
3397
3398 case VK_LEFT:
3399 MenuKeyLeft( &mt, wFlags );
3400 break;
3401
3402 case VK_RIGHT:
3403 MenuKeyRight( &mt, wFlags );
3404 break;
3405
3406 case VK_ESCAPE:
3407 fEndMenu = MenuKeyEscape(&mt, wFlags);
3408 break;
3409
3410 case VK_F1:
3411 {
3412 HELPINFO hi;
3413 hi.cbSize = sizeof(HELPINFO);
3414 hi.iContextType = HELPINFO_MENUITEM;
3415 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3416 {
3417 if (MenuInfo.FocusedItem == NO_SELECTED_ITEM)
3418 hi.iCtrlId = 0;
3419 else
3420 {
3421 MenuInitRosMenuItemInfo(&ItemInfo);
3422 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3423 MenuInfo.FocusedItem,
3424 &ItemInfo))
3425 {
3426 hi.iCtrlId = ItemInfo.wID;
3427 }
3428 else
3429 {
3430 hi.iCtrlId = 0;
3431 }
3432 MenuCleanupRosMenuItemInfo(&ItemInfo);
3433 }
3434 }
3435 hi.hItemHandle = hmenu;
3436 hi.dwContextId = MenuInfo.dwContextHelpID;
3437 hi.MousePos = msg.pt;
3438 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3439 break;
3440 }
3441
3442 default:
3443 break;
3444 }
3445 break; /* WM_KEYDOWN */
3446
3447 case WM_CHAR:
3448 case WM_SYSCHAR:
3449 {
3450 UINT pos;
3451
3452 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3453 if (msg.wParam == L'\r' || msg.wParam == L' ')
3454 {
3455 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3456 fEndMenu = (executedMenuId != -2);
3457 break;
3458 }
3459
3460 /* Hack to avoid control chars. */
3461 /* We will find a better way real soon... */
3462 if (msg.wParam < 32) break;
3463
3464 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
3465 LOWORD(msg.wParam), FALSE);
3466 if (pos == (UINT)-2) fEndMenu = TRUE;
3467 else if (pos == (UINT)-1) MessageBeep(0);
3468 else
3469 {
3470 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
3471 TRUE, 0);
3472 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3473 fEndMenu = (executedMenuId != -2);
3474 }
3475 }
3476 break;
3477 } /* switch(msg.message) - kbd */
3478 }
3479 else
3480 {
3481 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3482 DispatchMessageW( &msg );
3483 continue;
3484 }
3485
3486 if (!fEndMenu) fRemove = TRUE;
3487
3488 /* finally remove message from the queue */
3489
3490 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3491 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3492 else mt.TrackFlags &= ~TF_SKIPREMOVE;
3493 }
3494
3495 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3496 SetCapture(NULL); /* release the capture */
3497
3498 /* If dropdown is still painted and the close box is clicked on
3499 then the menu will be destroyed as part of the DispatchMessage above.
3500 This will then invalidate the menu handle in mt.hTopMenu. We should
3501 check for this first. */
3502 if( IsMenu( mt.TopMenu ) )
3503 {
3504 if (IsWindow(mt.OwnerWnd))
3505 {
3506 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3507 {
3508 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
3509
3510 if (MenuInfo.Flags & MNF_POPUP)
3511 {
3512 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
3513 DestroyWindow(MenuInfo.Wnd);
3514 MenuInfo.Wnd = NULL;
3515
3516 if (!(wFlags & TPM_NONOTIFY))
3517 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
3518 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
3519 }
3520 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3521 }
3522
3523 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
3524 }
3525
3526 /* Reset the variable for hiding menu */
3527 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3528 {
3529 MenuInfo.TimeToHide = FALSE;
3530 MenuSetRosMenuInfo(&MenuInfo);
3531 }
3532 }
3533
3534 /* The return value is only used by TrackPopupMenu */
3535 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3536 if (executedMenuId == -1) executedMenuId = 0;
3537 return executedMenuId;
3538 }
3539
3540 /***********************************************************************
3541 * MenuInitTracking
3542 */
3543 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3544 {
3545 ROSMENUINFO MenuInfo;
3546
3547 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3548
3549 HideCaret(0);
3550
3551 /* This makes the menus of applications built with Delphi work.
3552 * It also enables menus to be displayed in more than one window,
3553 * but there are some bugs left that need to be fixed in this case.
3554 */
3555 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
3556 {
3557 MenuInfo.Wnd = hWnd;
3558 MenuSetRosMenuInfo(&MenuInfo);
3559 }
3560
3561 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3562 if (!(wFlags & TPM_NONOTIFY))
3563 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3564
3565 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3566
3567 if (!(wFlags & TPM_NONOTIFY))
3568 {
3569 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3570 /* If an app changed/recreated menu bar entries in WM_INITMENU
3571 * menu sizes will be recalculated once the menu created/shown.
3572 */
3573
3574 if (!MenuInfo.Height)
3575 {
3576 /* app changed/recreated menu bar entries in WM_INITMENU
3577 Recalculate menu sizes else clicks will not work */
3578 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3579 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3580
3581 }
3582 }
3583
3584 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
3585 hWnd,
3586 MenuInfo.Flags & MNF_SYSDESKMN ? OBJID_SYSMENU : OBJID_MENU,
3587 CHILDID_SELF, 0);
3588 return TRUE;
3589 }
3590 /***********************************************************************
3591 * MenuExitTracking
3592 */
3593 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3594 {
3595 TRACE("hwnd=%p\n", hWnd);
3596
3597 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
3598 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3599 ShowCaret(0);
3600 top_popup = 0;
3601 top_popup_hmenu = NULL;
3602 return TRUE;
3603 }
3604
3605 /***********************************************************************
3606 * MenuTrackMouseMenuBar
3607 *
3608 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3609 */
3610 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3611 {
3612 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3613 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3614
3615 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3616
3617 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3618 if (IsMenu(hMenu))
3619 {
3620 /* map point to parent client coordinates */
3621 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3622 if (Parent != GetDesktopWindow())
3623 {
3624 ScreenToClient(Parent, &pt);
3625 }
3626
3627 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3628 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3629 MenuExitTracking(hWnd, FALSE);
3630 }
3631 }
3632
3633
3634 /***********************************************************************
3635 * MenuTrackKbdMenuBar
3636 *
3637 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3638 */
3639 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3640 {
3641 UINT uItem = NO_SELECTED_ITEM;
3642 HMENU hTrackMenu;
3643 ROSMENUINFO MenuInfo;
3644 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3645
3646 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3647
3648 /* find window that has a menu */
3649
3650 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
3651 (WS_CHILD | WS_POPUP)) != WS_CHILD))
3652 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3653
3654 /* check if we have to track a system menu */
3655
3656 hTrackMenu = GetMenu( hwnd );
3657 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3658 {
3659 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3660 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
3661 uItem = 0;
3662 wParam |= HTSYSMENU; /* prevent item lookup */
3663 }
3664
3665 if (!IsMenu( hTrackMenu )) return;
3666
3667 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3668
3669 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
3670 {
3671 goto track_menu;
3672 }
3673
3674 if( wChar && wChar != ' ' )
3675 {
3676 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
3677 if ( uItem >= (UINT)(-2) )
3678 {
3679 if( uItem == (UINT)(-1) ) MessageBeep(0);
3680 /* schedule end of menu tracking */
3681 wFlags |= TF_ENDMENU;
3682 goto track_menu;
3683 }
3684 }
3685
3686 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
3687
3688 if (!(wParam & HTSYSMENU) || wChar == ' ')
3689 {
3690 if( uItem == NO_SELECTED_ITEM )
3691 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
3692 else
3693 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3694 }
3695
3696 track_menu:
3697 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3698 MenuExitTracking( hwnd, FALSE );
3699 }
3700
3701 /**********************************************************************
3702 * TrackPopupMenuEx (USER32.@)
3703 */
3704 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
3705 HWND Wnd, LPTPMPARAMS Tpm)
3706 {
3707 BOOL ret = FALSE;
3708 ROSMENUINFO MenuInfo;
3709
3710 if (!IsMenu(Menu))
3711 {
3712 SetLastError( ERROR_INVALID_MENU_HANDLE );
3713 return FALSE;
3714 }
3715
3716 /* ReactOS Check */
3717 if (!ValidateHwnd(Wnd))
3718 {
3719 /* invalid window see wine menu.c test_menu_trackpopupmenu line 3146 */
3720 return FALSE;
3721 }
3722
3723 MenuGetRosMenuInfo(&MenuInfo, Menu);
3724 if (IsWindow(MenuInfo.Wnd))
3725 {
3726 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3727 return FALSE;
3728 }
3729
3730 MenuInitTracking(Wnd, Menu, TRUE, Flags);
3731
3732 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3733 if (!(Flags & TPM_NONOTIFY))
3734 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
3735
3736 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
3737 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
3738 Tpm ? &Tpm->rcExclude : NULL);
3739 MenuExitTracking(Wnd, TRUE);
3740 return ret;
3741 }
3742
3743 /**********************************************************************
3744 * TrackPopupMenu (USER32.@)
3745 */
3746 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
3747 int Reserved, HWND Wnd, CONST RECT *Rect)
3748 {
3749 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
3750 }
3751
3752 /*
3753 * From MSDN:
3754 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3755 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3756 *
3757 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3758 *
3759 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3760 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3761 * MFT_STRING is replaced by MIIM_STRING.
3762 * (So, I guess we should use MIIM_STRING only for strings?)
3763 *
3764 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3765 *
3766 * Based on wine, SetMenuItemInfo_common:
3767 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3768 * it will result in a error.
3769 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3770 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3771 *
3772 */
3773 static
3774 BOOL
3775 FASTCALL
3776 MenuSetItemData(
3777 LPMENUITEMINFOW mii,
3778 UINT Flags,
3779 UINT_PTR IDNewItem,
3780 LPCWSTR NewItem,
3781 BOOL Unicode)
3782 {
3783 /*
3784 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3785 */
3786 if(Flags & MF_BITMAP)
3787 {
3788 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
3789 mii->hbmpItem = (HBITMAP) NewItem;
3790
3791 if (Flags & MF_HELP)
3792 {
3793 /* increase ident */
3794 mii->fType |= MF_HELP;
3795 }
3796 }
3797 else if(Flags & MF_OWNERDRAW)
3798 {
3799 mii->fType |= MFT_OWNERDRAW;
3800 mii->fMask |= MIIM_DATA;
3801 mii->dwItemData = (DWORD_PTR) NewItem;
3802 }
3803 else if (Flags & MF_SEPARATOR)
3804 {
3805 mii->fType |= MFT_SEPARATOR;
3806 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3807 Flags |= MF_GRAYED|MF_DISABLED;
3808 }
3809 else /* Default action MF_STRING. */
3810 {
3811 /* Item beginning with a backspace is a help item */
3812 if (NewItem != NULL)
3813 {
3814 if (Unicode)
3815 {
3816 if (*NewItem == '\b')
3817 {
3818 mii->fType |= MF_HELP;
3819 NewItem++;
3820 }
3821 }
3822 else
3823 {
3824 LPCSTR NewItemA = (LPCSTR) NewItem;
3825 if (*NewItemA == '\b')
3826 {
3827 mii->fType |= MF_HELP;
3828 NewItemA++;
3829 NewItem = (LPCWSTR) NewItemA;
3830 }
3831 }
3832
3833 if (Flags & MF_HELP)
3834 mii->fType |= MF_HELP;
3835 mii->fMask |= MIIM_STRING;
3836 mii->fType |= MFT_STRING; /* Zero */
3837 mii->dwTypeData = (LPWSTR)NewItem;
3838 if (Unicode)
3839 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
3840 else
3841 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
3842 }
3843 else
3844 {
3845 mii->fType |= MFT_SEPARATOR;
3846 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3847 Flags |= MF_GRAYED|MF_DISABLED;
3848 }
3849 }
3850
3851 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
3852 {
3853 mii->fType |= MFT_RIGHTJUSTIFY;
3854 }
3855
3856 if(Flags & MF_MENUBREAK)
3857 {
3858 mii->fType |= MFT_MENUBREAK;
3859 }
3860 else if(Flags & MF_MENUBARBREAK)
3861 {
3862 mii->fType |= MFT_MENUBARBREAK;
3863 }
3864
3865 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
3866 {
3867 if (Flags & MF_GRAYED)
3868 mii->fState |= MF_GRAYED;
3869
3870 if (Flags & MF_DISABLED)
3871 mii->fState |= MF_DISABLED;
3872
3873 mii->fMask |= MIIM_STATE;
3874 }
3875 else if (Flags & MF_HILITE)
3876 {
3877 mii->fState |= MF_HILITE;
3878 mii->fMask |= MIIM_STATE;
3879 }
3880 else /* default state */
3881 {
3882 mii->fState |= MFS_ENABLED;
3883 mii->fMask |= MIIM_STATE;
3884 }
3885
3886 if(Flags & MF_POPUP)
3887 {
3888 mii->fMask |= MIIM_SUBMENU;
3889 mii->hSubMenu = (HMENU)IDNewItem;
3890 }
3891 else
3892 {
3893 mii->fMask |= MIIM_ID;
3894 mii->wID = (UINT)IDNewItem;
3895 }
3896 return TRUE;
3897 }
3898
3899 NTSTATUS WINAPI
3900 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
3901 {
3902 PLOADMENU_CALLBACK_ARGUMENTS Common;
3903 LRESULT Result;
3904
3905 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
3906
3907 Result = (LRESULT)LoadMenuW( Common->hModule,
3908 IS_INTRESOURCE(Common->MenuName[0]) ?
3909 MAKEINTRESOURCE(Common->MenuName[0]) :
3910 (LPCWSTR)&Common->MenuName);
3911
3912 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3913 }
3914
3915
3916 /* FUNCTIONS *****************************************************************/
3917
3918 /*static BOOL
3919 MenuIsStringItem(ULONG TypeData)
3920 {
3921 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3922 }*/
3923
3924
3925 /*
3926 * @implemented
3927 */
3928 BOOL WINAPI
3929 AppendMenuA(HMENU hMenu,
3930 UINT uFlags,
3931 UINT_PTR uIDNewItem,
3932 LPCSTR lpNewItem)
3933 {
3934 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3935 lpNewItem));
3936 }
3937
3938
3939 /*
3940 * @implemented
3941 */
3942 BOOL WINAPI
3943 AppendMenuW(HMENU hMenu,
3944 UINT uFlags,
3945 UINT_PTR uIDNewItem,
3946 LPCWSTR lpNewItem)
3947 {
3948 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3949 lpNewItem));
3950 }
3951
3952
3953 /*
3954 * @implemented
3955 */
3956 DWORD WINAPI
3957 CheckMenuItem(HMENU hmenu,
3958 UINT uIDCheckItem,
3959 UINT uCheck)
3960 {
3961 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3962 }
3963
3964 static
3965 BOOL
3966 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
3967 {
3968 UINT ItemCount, i;
3969 PROSMENUITEMINFO Items = NULL;
3970 UINT cChecked, cUnchecked;
3971 BOOL bRet = TRUE;
3972 //ROSMENUINFO mi;
3973
3974 if(idFirst > idLast)
3975 return FALSE;
3976
3977 ItemCount = GetMenuItemCount(hMenu);
3978
3979 //mi.cbSize = sizeof(ROSMENUINFO);
3980 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3981
3982
3983 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
3984 {
3985 ERR("MenuGetAllRosMenuItemInfo failed\n");
3986 return FALSE;
3987 }
3988
3989 cChecked = cUnchecked = 0;
3990
3991 for (i = 0 ; i < ItemCount; i++)
3992 {
3993 BOOL check = FALSE;
3994 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
3995 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
3996
3997 if ((Items[i].hSubMenu) && (uFlags == MF_BYCOMMAND))
3998 {
3999 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
4000 continue;
4001 }
4002 if (uFlags & MF_BYPOSITION)
4003 {
4004 if (i < idFirst || i > idLast)
4005 continue;
4006
4007 if (i == idCheck)
4008 {
4009 cChecked++;
4010 check = TRUE;
4011 }
4012 else
4013 {
4014 cUnchecked++;
4015 }
4016 }
4017 else
4018 {
4019 if (Items[i].wID < idFirst || Items[i].wID > idLast)
4020 continue;
4021
4022 if (Items[i].wID == idCheck)
4023 {
4024 cChecked++;
4025 check = TRUE;
4026 }
4027 else
4028 {
4029 cUnchecked++;
4030 }
4031 }
4032
4033 if (!bCheck)
4034 continue;
4035
4036 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
4037 if (check)
4038 {
4039 Items[i].fType |= MFT_RADIOCHECK;
4040 Items[i].fState |= MFS_CHECKED;
4041 }
4042 else
4043 {
4044 Items[i].fState &= ~MFS_CHECKED;
4045 }
4046
4047 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
4048 {
4049 ERR("MenuSetRosMenuItemInfo failed\n");
4050 bRet = FALSE;
4051 break;
4052 }
4053 }
4054 HeapFree(GetProcessHeap(), 0, Items);
4055
4056 *pChecked += cChecked;
4057 *pUnchecked += cUnchecked;
4058
4059 if (cChecked || cUnchecked)
4060 (*pMenuChanged)++;
4061
4062 return bRet;
4063 }
4064
4065 /*
4066 * @implemented
4067 */
4068 BOOL WINAPI
4069 CheckMenuRadioItem(HMENU hmenu,
4070 UINT idFirst,
4071 UINT idLast,
4072 UINT idCheck,
4073 UINT uFlags)
4074 {
4075 UINT cChecked = 0;
4076 UINT cUnchecked = 0;
4077 UINT cMenuChanged = 0;
4078
4079 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4080 return FALSE;
4081
4082 if (cMenuChanged > 1)
4083 return FALSE;
4084
4085 cMenuChanged = 0;
4086 cChecked = 0;
4087 cUnchecked = 0;
4088
4089 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4090 return FALSE;
4091
4092 return (cChecked != 0);
4093 }
4094
4095
4096 /*
4097 * @implemented
4098 */
4099 HMENU WINAPI
4100 CreateMenu(VOID)
4101 {
4102 MenuLoadBitmaps();
4103 return NtUserxCreateMenu();
4104 }
4105
4106
4107 /*
4108 * @implemented
4109 */
4110 HMENU WINAPI
4111 CreatePopupMenu(VOID)
4112 {
4113 MenuLoadBitmaps();
4114 return NtUserxCreatePopupMenu();
4115 }
4116
4117
4118 /*
4119 * @implemented
4120 */
4121 BOOL WINAPI
4122 DrawMenuBar(HWND hWnd)
4123 {
4124 // return NtUserxDrawMenuBar(hWnd);
4125 ROSMENUINFO MenuInfo;
4126 HMENU hMenu;
4127 hMenu = GetMenu(hWnd);
4128 if (!hMenu)
4129 return FALSE;
4130 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4131 MenuInfo.Height = 0; // make sure to recalc size
4132 MenuSetRosMenuInfo(&MenuInfo);
4133
4134 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4135 SWP_NOZORDER | SWP_FRAMECHANGED );
4136 return TRUE;
4137 }
4138
4139 /*
4140 * @implemented
4141 */
4142 BOOL WINAPI
4143 EnableMenuItem(HMENU hMenu,
4144 UINT uIDEnableItem,
4145 UINT uEnable)
4146 {
4147 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4148 }
4149
4150 /*
4151 * @implemented
4152 */
4153 BOOL WINAPI
4154 EndMenu(VOID)
4155 {
4156 GUITHREADINFO guii;
4157 guii.cbSize = sizeof(GUITHREADINFO);
4158 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4159 {
4160 if (!fEndMenu &&
4161 top_popup &&
4162 guii.hwndMenuOwner != top_popup )
4163 {
4164 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4165 }
4166 }
4167
4168 /* if we are in the menu code, and it is active */
4169 if (!fEndMenu && top_popup)
4170 {
4171 /* terminate the menu handling code */
4172 fEndMenu = TRUE;
4173
4174 /* needs to be posted to wakeup the internal menu handler */
4175 /* which will now terminate the menu, in the event that */
4176 /* the main window was minimized, or lost focus, so we */
4177 /* don't end up with an orphaned menu */
4178 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4179 }
4180 return TRUE;
4181 }
4182
4183 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4184 UINT wHilite )
4185 {
4186 ROSMENUINFO MenuInfo;
4187 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4188 // Force bits to be set call server side....
4189 // This alone works and passes all the menu test_menu_hilitemenuitem tests.
4190 if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE;
4191 // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967
4192 // Now redraw menu.
4193 if (!MenuGetRosMenuInfo(&MenuInfo, hMenu)) return FALSE;
4194 if (MenuInfo.FocusedItem == wItemID) return TRUE;
4195 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4196 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4197 return TRUE;
4198 }
4199
4200 /*
4201 * @implemented
4202 */
4203 HMENU WINAPI
4204 GetMenu(HWND hWnd)
4205 {
4206 PWND Wnd = ValidateHwnd(hWnd);
4207
4208 if (!Wnd)
4209 return NULL;
4210
4211 return UlongToHandle(Wnd->IDMenu);
4212 }
4213
4214
4215 /*
4216 * @implemented
4217 */
4218 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4219 {
4220 BOOL Ret;
4221 Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
4222 // Reason to move to server side!!!!!
4223 if (!Ret) return Ret;
4224 // EL HAXZO!!!
4225 pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
4226 if (!idItem)
4227 {
4228 pmbi->fFocused = pmbi->fBarFocused;
4229 }
4230
4231 return TRUE;
4232 }
4233
4234
4235 /*
4236 * @implemented
4237 */
4238 LONG WINAPI
4239 GetMenuCheckMarkDimensions(VOID)
4240 {
4241 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4242 GetSystemMetrics(SM_CYMENUCHECK)));
4243 }
4244
4245
4246 /*
4247 * @implemented
4248 */
4249 UINT WINAPI
4250 GetMenuDefaultItem(HMENU hMenu,
4251 UINT fByPos,
4252 UINT gmdiFlags)
4253 {
4254 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4255 }
4256
4257
4258 /*
4259 * @implemented
4260 */
4261 BOOL WINAPI
4262 GetMenuInfo(HMENU hmenu,
4263 LPMENUINFO lpcmi)
4264 {
4265 ROSMENUINFO mi;
4266 BOOL res = FALSE;
4267
4268 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4269 return FALSE;
4270
4271 RtlZeroMemory(&mi, sizeof(MENUINFO));
4272 mi.cbSize = sizeof(MENUINFO);
4273 mi.fMask = lpcmi->fMask;
4274
4275 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4276
4277 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4278 return res;
4279 }
4280
4281
4282 /*
4283 * @implemented
4284 */
4285 int WINAPI
4286 GetMenuItemCount(HMENU Menu)
4287 {
4288 ROSMENUINFO MenuInfo;
4289
4290 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4291 }
4292
4293
4294 /*
4295 * @implemented
4296 */
4297 UINT WINAPI
4298 GetMenuItemID(HMENU hMenu,
4299 int nPos)
4300 {
4301 ROSMENUITEMINFO mii;
4302
4303 mii.cbSize = sizeof(MENUITEMINFOW);
4304 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4305
4306 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4307 {
4308 return -1;
4309 }
4310
4311 if (NULL != mii.hSubMenu)
4312 {
4313 return -1;
4314 }
4315 if (0 == mii.wID)
4316 {
4317 return -1;
4318 }
4319
4320 return mii.wID;
4321 }
4322
4323
4324 /*
4325 * @implemented
4326 */
4327 BOOL WINAPI
4328 GetMenuItemInfoA(
4329 HMENU Menu,
4330 UINT Item,
4331 BOOL ByPosition,
4332 LPMENUITEMINFOA mii)
4333 {
4334 MENUITEMINFOW miiW;
4335 LPSTR AnsiBuffer;
4336 INT Count;
4337
4338 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4339 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4340 {
4341 SetLastError(ERROR_INVALID_PARAMETER);
4342 return FALSE;
4343 }
4344
4345 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4346 {
4347 /* No text requested, just pass on */
4348 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4349 }
4350
4351 AnsiBuffer = mii->dwTypeData;
4352 Count = miiW.cch = mii->cch;
4353 RtlCopyMemory(&miiW, mii, mii->cbSize);
4354 miiW.dwTypeData = 0;
4355
4356 if (AnsiBuffer)
4357 {
4358 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4359 miiW.cch * sizeof(WCHAR));
4360 if (miiW.dwTypeData == NULL) return FALSE;
4361 miiW.dwTypeData[0] = 0;
4362 }
4363
4364 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4365 {
4366 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4367 return FALSE;
4368 }
4369
4370 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4371
4372 if (!AnsiBuffer || !Count)
4373 {
4374 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4375 mii->dwTypeData = AnsiBuffer;
4376 mii->cch = miiW.cch;
4377 return TRUE;
4378 }
4379
4380 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4381 {
4382 if (miiW.cch)
4383 {
4384 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4385 {
4386 AnsiBuffer[0] = 0;
4387 }
4388 if (Count > miiW.cch)
4389 {
4390 AnsiBuffer[miiW.cch] = 0;
4391 }
4392 mii->cch = miiW.cch;
4393 }
4394 }
4395 else
4396 {
4397 AnsiBuffer[0] = 0;
4398 }
4399
4400 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4401 mii->dwTypeData = AnsiBuffer;
4402
4403 return TRUE;
4404 }
4405
4406
4407 /*
4408 * @implemented
4409 */
4410 BOOL WINAPI
4411 GetMenuItemInfoW(
4412 HMENU Menu,
4413 UINT Item,
4414 BOOL ByPosition,
4415 LPMENUITEMINFOW mii)
4416 {
4417 MENUITEMINFOW miiW;
4418 LPWSTR String;
4419 INT Count;
4420
4421 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4422 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4423 {
4424 SetLastError(ERROR_INVALID_PARAMETER);
4425 return FALSE;
4426 }
4427
4428 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4429 {
4430 /* No text requested, just pass on */
4431 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4432 }
4433
4434 String = mii->dwTypeData;
4435 Count = mii->cch;
4436 RtlCopyMemory(&miiW, mii, mii->cbSize);
4437 miiW.dwTypeData = 0;
4438
4439 if (String)
4440 {
4441 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4442 miiW.cch * sizeof(WCHAR));
4443 if (miiW.dwTypeData == NULL) return FALSE;
4444 miiW.dwTypeData[0] = 0;
4445 }
4446
4447 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4448 {
4449 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4450 return FALSE;
4451 }
4452
4453 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4454
4455 if (!String || !Count)
4456 {
4457 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4458 mii->dwTypeData = String; // may not be zero.
4459 mii->cch = miiW.cch;
4460 return TRUE;
4461 }
4462
4463 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4464 {
4465 lstrcpynW( String, miiW.dwTypeData, Count );
4466 }
4467
4468 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4469 mii->dwTypeData = String;
4470 mii->cch = strlenW(String);
4471 return TRUE;
4472 }
4473
4474
4475 /*
4476 * @implemented
4477 */
4478 UINT
4479 WINAPI
4480 GetMenuState(
4481 HMENU hMenu,
4482 UINT uId,
4483 UINT uFlags)
4484 {
4485 ROSMENUINFO MenuInfo;
4486 ROSMENUITEMINFO mii;
4487 memset( &mii, 0, sizeof(mii) );
4488 mii.cbSize = sizeof(MENUITEMINFOW);
4489 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4490
4491 SetLastError(0);
4492 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4493 {
4494 UINT nSubItems = 0;
4495 if(mii.hSubMenu)
4496 {
4497 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4498 {
4499 return (UINT) -1;
4500 }
4501 nSubItems = MenuInfo.MenuItemCount;
4502
4503 /* FIXME - ported from wine, does that work (0xff)? */
4504 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4505 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4506
4507 return (UINT)-1; /* Invalid submenu */
4508 }
4509
4510 /* FIXME - ported from wine, does that work? */
4511 return (mii.fType | mii.fState);
4512 }
4513
4514 return (UINT)-1;
4515 }
4516
4517
4518 /*
4519 * @implemented
4520 */
4521 int
4522 WINAPI
4523 GetMenuStringA(
4524 HMENU hMenu,
4525 UINT uIDItem,
4526 LPSTR lpString,
4527 int nMaxCount,
4528 UINT uFlag)
4529 {
4530 MENUITEMINFOA mii;
4531 memset( &mii, 0, sizeof(mii) );
4532 mii.dwTypeData = lpString;
4533 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4534 mii.fType = MFT_STRING;
4535 mii.cbSize = sizeof(MENUITEMINFOA);
4536 mii.cch = nMaxCount;
4537
4538 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4539 return 0;
4540 else
4541 return mii.cch;
4542 }
4543
4544
4545 /*
4546 * @implemented
4547 */
4548 int
4549 WINAPI
4550 GetMenuStringW(
4551 HMENU hMenu,
4552 UINT uIDItem,
4553 LPWSTR lpString,
4554 int nMaxCount,
4555 UINT uFlag)
4556 {
4557 MENUITEMINFOW miiW;
4558 memset( &miiW, 0, sizeof(miiW) );
4559 miiW.dwTypeData = lpString;
4560 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4561 miiW.fType = MFT_STRING;
4562 miiW.cbSize = sizeof(MENUITEMINFOW);
4563 miiW.cch = nMaxCount;
4564
4565 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4566 return 0;
4567 else
4568 return miiW.cch;
4569 }
4570
4571
4572 /*
4573 * @implemented
4574 */
4575 HMENU
4576 WINAPI
4577 GetSubMenu(
4578 HMENU hMenu,
4579 int nPos)
4580 {
4581 ROSMENUITEMINFO mi;
4582
4583 mi.cbSize = sizeof(MENUITEMINFOW);
4584 mi.fMask = MIIM_SUBMENU;
4585
4586 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4587 {
4588 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4589 }
4590
4591 return NULL;
4592 }
4593
4594 /*
4595 * @implemented
4596 */
4597 HMENU
4598 WINAPI
4599 GetSystemMenu(
4600 HWND hWnd,
4601 BOOL bRevert)
4602 {
4603 HMENU TopMenu;
4604
4605 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4606
4607 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4608 }
4609
4610
4611 /*
4612 * @implemented
4613 */
4614 BOOL
4615 WINAPI
4616 InsertMenuA(
4617 HMENU hMenu,
4618 UINT uPosition,
4619 UINT uFlags,
4620 UINT_PTR uIDNewItem,
4621 LPCSTR lpNewItem)
4622 {
4623 MENUITEMINFOA mii;
4624 memset( &mii, 0, sizeof(mii) );
4625 mii.cbSize = sizeof(MENUITEMINFOA);
4626 mii.fMask = MIIM_FTYPE;
4627
4628 MenuSetItemData((LPMENUITEMINFOW) &mii,
4629 uFlags,
4630 uIDNewItem,
4631 (LPCWSTR) lpNewItem,
4632 FALSE);
4633
4634 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4635 }
4636
4637
4638
4639 /*
4640 * @implemented
4641 */
4642 BOOL
4643 WINAPI
4644 InsertMenuItemA(
4645 HMENU hMenu,
4646 UINT uItem,
4647 BOOL fByPosition,
4648 LPCMENUITEMINFOA lpmii)
4649 {
4650 MENUITEMINFOW mi;
4651 UNICODE_STRING MenuText;
4652 BOOL res = FALSE;
4653 BOOL CleanHeap = FALSE;
4654
4655 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4656 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4657 {
4658 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4659
4660 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4661 {
4662 mi.cbSize = sizeof( MENUITEMINFOW);
4663 mi.hbmpItem = NULL;
4664 }
4665 /* copy the text string */
4666 if (((mi.fMask & MIIM_STRING) ||
4667 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4668 && mi.dwTypeData != NULL)
4669 {
4670 if (!RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData))
4671 {
4672 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
4673 return FALSE;
4674 }
4675 mi.dwTypeData = MenuText.Buffer;
4676 mi.cch = MenuText.Length / sizeof(WCHAR);
4677 CleanHeap = TRUE;
4678 }
4679 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4680
4681 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4682 }
4683 return res;
4684 }
4685
4686
4687 /*
4688 * @implemented
4689 */
4690 BOOL
4691 WINAPI
4692 InsertMenuItemW(
4693 HMENU hMenu,
4694 UINT uItem,
4695 BOOL fByPosition,
4696 LPCMENUITEMINFOW lpmii)
4697 {
4698 MENUITEMINFOW mi;
4699 UNICODE_STRING MenuText;
4700 BOOL res = FALSE;
4701
4702 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4703 if a bad user passes bad data, we crash his process instead of the
4704 entire kernel */
4705
4706 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4707 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4708 {
4709 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4710
4711 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4712 {
4713 mi.cbSize = sizeof( MENUITEMINFOW);
4714 mi.hbmpItem = NULL;
4715 }
4716 /* copy the text string */
4717 if (((mi.fMask & MIIM_STRING) ||
4718 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4719 && mi.dwTypeData != NULL)
4720 {
4721 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4722 mi.dwTypeData = MenuText.Buffer;
4723 mi.cch = MenuText.Length / sizeof(WCHAR);
4724 }
4725 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4726 }
4727 return res;
4728 }
4729
4730
4731 /*
4732 * @implemented
4733 */
4734 BOOL
4735 WINAPI
4736 InsertMenuW(
4737 HMENU hMenu,
4738 UINT uPosition,
4739 UINT uFlags,
4740 UINT_PTR uIDNewItem,
4741 LPCWSTR lpNewItem)
4742 {
4743 MENUITEMINFOW mii;
4744 memset( &mii, 0, sizeof(mii) );
4745 mii.cbSize = sizeof(MENUITEMINFOW);
4746 mii.fMask = MIIM_FTYPE;
4747
4748 MenuSetItemData( &mii,
4749 uFlags,
4750 uIDNewItem,
4751 lpNewItem,
4752 TRUE);
4753
4754 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4755 }
4756
4757
4758 /*
4759 * @implemented
4760 */
4761 BOOL
4762 WINAPI
4763 IsMenu(
4764 HMENU Menu)
4765 {
4766 if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
4767 return FALSE;
4768 }
4769
4770
4771 /*
4772 * @implemented
4773 */
4774 HMENU WINAPI
4775 LoadMenuA(HINSTANCE hInstance,
4776 LPCSTR lpMenuName)
4777 {
4778 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4779 if (Resource == NULL)
4780 {
4781 return(NULL);
4782 }
4783 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4784 }
4785
4786
4787 /*
4788 * @implemented
4789 */
4790 HMENU WINAPI
4791 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4792 {
4793 return(LoadMenuIndirectW(lpMenuTemplate));
4794 }
4795
4796
4797 /*
4798 * @implemented
4799 */
4800 HMENU WINAPI
4801 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4802 {
4803 HMENU hMenu;
4804 WORD version, offset;
4805 LPCSTR p = (LPCSTR)lpMenuTemplate;
4806
4807 version = GET_WORD(p);
4808 p += sizeof(WORD);
4809
4810 switch (version)
4811 {
4812 case 0: /* standard format is version of 0 */
4813 offset = GET_WORD(p);
4814 p += sizeof(WORD) + offset;
4815 if (!(hMenu = CreateMenu())) return 0;
4816 if (!MENU_ParseResource(p, hMenu))
4817 {
4818 DestroyMenu(hMenu);
4819 return 0;
4820 }
4821 return hMenu;
4822 case 1: /* extended format is version of 1 */
4823 offset = GET_WORD(p);
4824 p += sizeof(WORD) + offset;
4825 if (!(hMenu = CreateMenu())) return 0;
4826 if (!MENUEX_ParseResource(p, hMenu))
4827 {
4828 DestroyMenu( hMenu );
4829 return 0;
4830 }
4831 return hMenu;
4832 default:
4833 ERR("Menu template version %d not supported.\n", version);
4834 return 0;
4835 }
4836 }
4837
4838
4839 /*
4840 * @implemented
4841 */
4842 HMENU WINAPI
4843 LoadMenuW(HINSTANCE hInstance,
4844 LPCWSTR lpMenuName)
4845 {
4846 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4847 if (Resource == NULL)
4848 {
4849 return(NULL);
4850 }
4851 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4852 }
4853
4854
4855 /*
4856 * @implemented
4857 */
4858 int
4859 WINAPI
4860 MenuItemFromPoint(
4861 HWND hWnd,
4862 HMENU hMenu,
4863 POINT ptScreen)
4864 {
4865 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4866 }
4867
4868
4869 /*
4870 * @implemented
4871 */
4872 BOOL
4873 WINAPI
4874 ModifyMenuA(
4875 HMENU hMnu,
4876 UINT uPosition,
4877 UINT uFlags,
4878 UINT_PTR uIDNewItem,
4879 LPCSTR lpNewItem)
4880 {
4881 ROSMENUINFO mi;
4882 ROSMENUITEMINFO rmii;
4883 MENUITEMINFOA mii;
4884 memset( &mii, 0, sizeof(mii) );
4885 mii.cbSize = sizeof(MENUITEMINFOA);
4886 mii.fMask = MIIM_FTYPE;
4887
4888 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4889
4890 mi.Height = 0;
4891
4892 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4893
4894 MenuInitRosMenuItemInfo( &rmii );
4895
4896 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4897
4898 if (rmii.hSubMenu && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4899 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4900
4901 MenuCleanupRosMenuItemInfo( &rmii );
4902
4903 MenuSetItemData((LPMENUITEMINFOW) &mii,
4904 uFlags,
4905 uIDNewItem,
4906 (LPCWSTR) lpNewItem,
4907 FALSE);
4908
4909 return SetMenuItemInfoA( hMnu,
4910 uPosition,
4911 (BOOL)(MF_BYPOSITION & uFlags),
4912 &mii);
4913 }
4914
4915
4916 /*
4917 * @implemented
4918 */
4919 BOOL
4920 WINAPI
4921 ModifyMenuW(
4922 HMENU hMnu,
4923 UINT uPosition,
4924 UINT uFlags,
4925 UINT_PTR uIDNewItem,
4926 LPCWSTR lpNewItem)
4927 {
4928 ROSMENUINFO mi;
4929 ROSMENUITEMINFO rmii;
4930 MENUITEMINFOW mii;
4931 memset ( &mii, 0, sizeof(mii) );
4932 mii.cbSize = sizeof(MENUITEMINFOW);
4933 mii.fMask = MIIM_FTYPE;
4934
4935 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4936
4937 mi.Height = 0; // Force size recalculation.
4938
4939 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4940
4941 MenuInitRosMenuItemInfo( &rmii );
4942
4943 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4944
4945 if (rmii.hSubMenu && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4946 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4947
4948 MenuCleanupRosMenuItemInfo( &rmii );
4949
4950 /* Init new data for this menu item */
4951 MenuSetItemData( &mii,
4952 uFlags,
4953 uIDNewItem,
4954 lpNewItem,
4955 TRUE);
4956
4957 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4958 return SetMenuItemInfoW( hMnu,
4959 uPosition,
4960 (BOOL)(MF_BYPOSITION & uFlags),
4961 &mii);
4962 }
4963
4964
4965 /*
4966 * @implemented
4967 */
4968 BOOL WINAPI
4969 SetMenu(HWND hWnd,
4970 HMENU hMenu)
4971 {
4972 return NtUserSetMenu(hWnd, hMenu, TRUE);
4973 }
4974
4975
4976 /*
4977 * @implemented
4978 */
4979 BOOL
4980 WINAPI
4981 SetMenuInfo(
4982 HMENU hmenu,
4983 LPCMENUINFO lpcmi)
4984 {
4985 ROSMENUINFO mi;
4986 BOOL res = FALSE;
4987
4988 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4989 {
4990 SetLastError(ERROR_INVALID_PARAMETER);
4991 return res;
4992 }
4993
4994 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4995 return NtUserMenuInfo(hmenu, &mi, TRUE);
4996 }
4997
4998
4999 /*
5000 * @implemented
5001 */
5002 BOOL
5003 WINAPI
5004 SetMenuItemBitmaps(
5005 HMENU hMenu,
5006 UINT uPosition,
5007 UINT uFlags,
5008 HBITMAP hBitmapUnchecked,
5009 HBITMAP hBitmapChecked)
5010 {
5011 ROSMENUITEMINFO uItem;
5012 memset ( &uItem, 0, sizeof(uItem) );
5013 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
5014
5015 if(!(NtUserMenuItemInfo(hMenu, uPosition,
5016 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
5017
5018 if (!hBitmapChecked && !hBitmapUnchecked)
5019 {
5020 uItem.fState &= ~MF_USECHECKBITMAPS;
5021 }
5022 else /* Install new bitmaps */
5023 {
5024 uItem.hbmpChecked = hBitmapChecked;
5025 uItem.hbmpUnchecked = hBitmapUnchecked;
5026 uItem.fState |= MF_USECHECKBITMAPS;
5027 }
5028 return NtUserMenuItemInfo(hMenu, uPosition,
5029 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
5030 }
5031
5032
5033 /*
5034 * @implemented
5035 */
5036 BOOL
5037 WINAPI
5038 SetMenuItemInfoA(
5039 HMENU hMenu,
5040 UINT uItem,
5041 BOOL fByPosition,
5042 LPCMENUITEMINFOA lpmii)
5043 {
5044 MENUITEMINFOW MenuItemInfoW;
5045 UNICODE_STRING UnicodeString;
5046 ULONG Result = FALSE;
5047
5048 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5049
5050 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5051 {
5052 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5053 MenuItemInfoW.hbmpItem = NULL;
5054 }
5055 /*
5056 * MIIM_STRING == good
5057 * MIIM_TYPE & MFT_STRING == good
5058 * MIIM_STRING & MFT_STRING == good
5059 * MIIM_STRING & MFT_OWNERSRAW == good
5060 */
5061 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5062 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5063 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5064 && MenuItemInfoW.dwTypeData != NULL)
5065 {
5066 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5067 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
5068 (LPSTR)MenuItemInfoW.dwTypeData))
5069 {
5070 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5071 return FALSE;
5072 }
5073 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
5074 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
5075 }
5076 else
5077 {
5078 UnicodeString.Buffer = NULL;
5079 }
5080
5081 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5082 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5083
5084 if (UnicodeString.Buffer != NULL)
5085 {
5086 RtlFreeUnicodeString(&UnicodeString);
5087 }
5088
5089 return Result;
5090 }
5091
5092
5093 /*
5094 * @implemented
5095 */
5096 BOOL
5097 WINAPI
5098 SetMenuItemInfoW(
5099 HMENU hMenu,
5100 UINT uItem,
5101 BOOL fByPosition,
5102 LPCMENUITEMINFOW lpmii)
5103 {
5104 MENUITEMINFOW MenuItemInfoW;
5105 ULONG Result;
5106
5107 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5108
5109 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5110 {
5111 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5112 MenuItemInfoW.hbmpItem = NULL;
5113 }
5114
5115 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5116 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5117 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5118 && MenuItemInfoW.dwTypeData != NULL)
5119 {
5120 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5121 }
5122 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5123 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5124
5125 return Result;
5126 }
5127
5128 /*
5129 * @implemented
5130 */
5131 BOOL
5132 WINAPI
5133 SetSystemMenu (
5134 HWND hwnd,
5135 HMENU hMenu)
5136 {
5137 if(!hwnd)
5138 {
5139 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5140 return FALSE;
5141 }
5142 if(!hMenu)
5143 {
5144 SetLastError(ERROR_INVALID_MENU_HANDLE);
5145 return FALSE;
5146 }
5147 return NtUserSetSystemMenu(hwnd, hMenu);
5148 }
5149
5150 //
5151 // Example for the Win32/User32 rewrite.
5152 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5153 //
5154 //
5155 BOOL
5156 WINAPI
5157 NEWTrackPopupMenu(
5158 HMENU Menu,
5159 UINT Flags,
5160 int x,
5161 int y,
5162 int Reserved,
5163 HWND Wnd,
5164 CONST RECT *Rect)
5165 {
5166 return NtUserTrackPopupMenuEx( Menu,
5167 Flags,
5168 x,
5169 y,
5170 Wnd,
5171 NULL); // LPTPMPARAMS is null
5172 }
5173
5174
5175 /*
5176 * @implemented
5177 */
5178 DWORD
5179 WINAPI
5180 GetMenuContextHelpId(HMENU hmenu)
5181 {
5182 ROSMENUINFO mi;
5183 mi.cbSize = sizeof(ROSMENUINFO);
5184 mi.fMask = MIM_HELPID;
5185
5186 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5187 {
5188 return mi.dwContextHelpID;
5189 }
5190 return 0;
5191 }
5192
5193 /*
5194 * @unimplemented
5195 */
5196 BOOL
5197 WINAPI
5198 MenuWindowProcA(
5199 HWND hWnd,
5200 ULONG_PTR Result,
5201 UINT Msg,
5202 WPARAM wParam,
5203 LPARAM lParam
5204 )
5205 {
5206 if ( Msg < WM_USER)
5207 {
5208 LRESULT lResult;
5209 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5210 if (Result)
5211 {
5212 Result = (ULONG_PTR)lResult;
5213 return TRUE;
5214 }
5215 return FALSE;
5216 }
5217 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5218
5219 }
5220
5221 /*
5222 * @unimplemented
5223 */
5224 BOOL
5225 WINAPI
5226 MenuWindowProcW(
5227 HWND hWnd,
5228 ULONG_PTR Result,
5229 UINT Msg,
5230 WPARAM wParam,
5231 LPARAM lParam
5232 )
5233 {
5234 if ( Msg < WM_USER)
5235 {
5236 LRESULT lResult;
5237 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5238 if (Result)
5239 {
5240 Result = (ULONG_PTR)lResult;
5241 return TRUE;
5242 }
5243 return FALSE;
5244 }
5245 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5246 }
5247
5248 /*
5249 * @implemented
5250 */
5251 BOOL
5252 WINAPI
5253 ChangeMenuW(
5254 HMENU hMenu,
5255 UINT cmd,
5256 LPCWSTR lpszNewItem,
5257 UINT cmdInsert,
5258 UINT flags)
5259 {
5260 /*
5261 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5262 for MF_DELETE. We should check the parameters for all others
5263 MF_* actions also (anybody got a doc on ChangeMenu?).
5264 */
5265
5266 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5267 {
5268 case MF_APPEND :
5269 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5270
5271 case MF_DELETE :
5272 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5273
5274 case MF_CHANGE :
5275 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5276
5277 case MF_REMOVE :
5278 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5279 flags &~ MF_REMOVE);
5280
5281 default : /* MF_INSERT */
5282 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5283 };
5284 }
5285
5286 /*
5287 * @implemented
5288 */
5289 BOOL
5290 WINAPI
5291 ChangeMenuA(
5292 HMENU hMenu,
5293 UINT cmd,
5294 LPCSTR lpszNewItem,
5295 UINT cmdInsert,
5296 UINT flags)
5297 {
5298 /*
5299 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5300 for MF_DELETE. We should check the parameters for all others
5301 MF_* actions also (anybody got a doc on ChangeMenu?).
5302 */
5303
5304 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5305 {
5306 case MF_APPEND :
5307 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5308
5309 case MF_DELETE :
5310 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5311
5312 case MF_CHANGE :
5313 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5314
5315 case MF_REMOVE :
5316 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5317 flags &~ MF_REMOVE);
5318
5319 default : /* MF_INSERT */
5320 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5321 };
5322 }
5323
5324
5325
5326
5327