[User32]
[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.fType & MF_POPUP)) 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->fType & MF_POPUP)
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->fType & MF_POPUP)
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.fType & MF_POPUP ? wIndex : ItemInfo.wID,
1710 ItemInfo.fType | ItemInfo.fState |
1711 (hmenu->Flags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
1712
1713 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
1714 }
1715 }
1716 }
1717 else if (sendMenuSelect) {
1718 if(topmenu) {
1719 int pos;
1720 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
1721 if (pos != NO_SELECTED_ITEM)
1722 {
1723 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
1724 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
1725 {
1726 WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
1727 (TopMenuInfo.Flags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
1728
1729 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
1730 }
1731 }
1732 }
1733 }
1734 MenuCleanupRosMenuItemInfo(&ItemInfo);
1735 ReleaseDC(hmenu->Wnd, hdc);
1736 }
1737
1738 /***********************************************************************
1739 * MenuMoveSelection
1740 *
1741 * Moves currently selected item according to the Offset parameter.
1742 * If there is no selection then it should select the last item if
1743 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1744 */
1745 static void FASTCALL
1746 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1747 {
1748 INT i;
1749 ROSMENUITEMINFO ItemInfo;
1750 INT OrigPos;
1751
1752 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1753
1754 /* Prevent looping */
1755 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1756 return;
1757 else if (Offset < -1)
1758 Offset = -1;
1759 else if (Offset > 1)
1760 Offset = 1;
1761
1762 MenuInitRosMenuItemInfo(&ItemInfo);
1763
1764 OrigPos = MenuInfo->FocusedItem;
1765 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1766 {
1767 OrigPos = 0;
1768 i = -1;
1769 }
1770 else
1771 {
1772 i = MenuInfo->FocusedItem;
1773 }
1774
1775 do
1776 {
1777 /* Step */
1778 i += Offset;
1779 /* Clip and wrap around */
1780 if (i < 0)
1781 {
1782 i = MenuInfo->MenuItemCount - 1;
1783 }
1784 else if (i >= MenuInfo->MenuItemCount)
1785 {
1786 i = 0;
1787 }
1788 /* If this is a good candidate; */
1789 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1790 0 == (ItemInfo.fType & MF_SEPARATOR))
1791 {
1792 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1793 MenuCleanupRosMenuItemInfo(&ItemInfo);
1794 return;
1795 }
1796 } while (i != OrigPos);
1797
1798 /* Not found */
1799 MenuCleanupRosMenuItemInfo(&ItemInfo);
1800 }
1801
1802 //
1803 // This breaks some test results. Should handle A2U if called!
1804 //
1805 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1806 {
1807 #ifdef __REACTOS__
1808 PWND pWnd;
1809
1810 pWnd = ValidateHwnd(Wnd);
1811 if (pWnd)
1812 {
1813 if (!pWnd->fnid)
1814 {
1815 NtUserSetWindowFNID(Wnd, FNID_MENU);
1816 }
1817 else
1818 {
1819 if (pWnd->fnid != FNID_MENU)
1820 {
1821 ERR("Wrong window class for Menu!\n");
1822 return 0;
1823 }
1824 }
1825 }
1826 #endif
1827
1828 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1829
1830 switch(Message)
1831 {
1832 case WM_CREATE:
1833 {
1834 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1835 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1836 return 0;
1837 }
1838
1839 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1840 return MA_NOACTIVATE;
1841
1842 case WM_PAINT:
1843 {
1844 PAINTSTRUCT ps;
1845 BeginPaint(Wnd, &ps);
1846 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1847 EndPaint(Wnd, &ps);
1848 return 0;
1849 }
1850
1851 case WM_PRINTCLIENT:
1852 {
1853 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1854 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1855 return 0;
1856 }
1857
1858 case WM_ERASEBKGND:
1859 return 1;
1860
1861 case WM_DESTROY:
1862 /* zero out global pointer in case resident popup window was destroyed. */
1863 if (Wnd == top_popup)
1864 {
1865 top_popup = NULL;
1866 top_popup_hmenu = NULL;
1867 }
1868 break;
1869
1870 #ifdef __REACTOS__
1871 case WM_NCDESTROY:
1872 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
1873 break;
1874 #endif
1875
1876 case WM_SHOWWINDOW:
1877 if (0 != wParam)
1878 {
1879 if (0 == GetWindowLongPtrA(Wnd, 0))
1880 {
1881 OutputDebugStringA("no menu to display\n");
1882 }
1883 }
1884 else
1885 {
1886 SetWindowLongPtrA(Wnd, 0, 0);
1887 }
1888 break;
1889
1890 case MM_SETMENUHANDLE:
1891 SetWindowLongPtrA(Wnd, 0, wParam);
1892 break;
1893
1894 case MM_GETMENUHANDLE:
1895 case MN_GETHMENU:
1896 return GetWindowLongPtrA(Wnd, 0);
1897
1898 default:
1899 return DefWindowProcA(Wnd, Message, wParam, lParam);
1900 }
1901 return 0;
1902 }
1903
1904 LRESULT WINAPI
1905 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1906 {
1907 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
1908 PWND pWnd;
1909
1910 pWnd = ValidateHwnd(Wnd);
1911 if (pWnd)
1912 {
1913 if (!pWnd->fnid)
1914 {
1915 if (Message != WM_NCCREATE)
1916 {
1917 return DefWindowProcW(Wnd, Message, wParam, lParam);
1918 }
1919 NtUserSetWindowFNID(Wnd, FNID_MENU);
1920 }
1921 else
1922 {
1923 if (pWnd->fnid != FNID_MENU)
1924 {
1925 ERR("Wrong window class for Menu!\n");
1926 return 0;
1927 }
1928 }
1929 }
1930 #endif
1931
1932 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1933
1934 switch(Message)
1935 {
1936 case WM_CREATE:
1937 {
1938 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1939 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1940 return 0;
1941 }
1942
1943 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1944 return MA_NOACTIVATE;
1945
1946 case WM_PAINT:
1947 {
1948 PAINTSTRUCT ps;
1949 BeginPaint(Wnd, &ps);
1950 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1951 EndPaint(Wnd, &ps);
1952 return 0;
1953 }
1954
1955 case WM_PRINTCLIENT:
1956 {
1957 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1958 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1959 return 0;
1960 }
1961
1962 case WM_ERASEBKGND:
1963 return 1;
1964
1965 case WM_DESTROY:
1966 /* zero out global pointer in case resident popup window was destroyed. */
1967 if (Wnd == top_popup)
1968 {
1969 top_popup = NULL;
1970 top_popup_hmenu = NULL;
1971 }
1972 break;
1973
1974 case WM_SHOWWINDOW:
1975 if (0 != wParam)
1976 {
1977 if (0 == GetWindowLongPtrW(Wnd, 0))
1978 {
1979 OutputDebugStringA("no menu to display\n");
1980 }
1981 }
1982 else
1983 {
1984 SetWindowLongPtrW(Wnd, 0, 0);
1985 }
1986 break;
1987
1988 case MM_SETMENUHANDLE:
1989 SetWindowLongPtrW(Wnd, 0, wParam);
1990 break;
1991
1992 case MM_GETMENUHANDLE:
1993 case MN_GETHMENU:
1994 return GetWindowLongPtrW(Wnd, 0);
1995
1996 default:
1997 return DefWindowProcW(Wnd, Message, wParam, lParam);
1998 }
1999
2000 return 0;
2001 }
2002
2003 /**********************************************************************
2004 * MENU_ParseResource
2005 *
2006 * Parse a standard menu resource and add items to the menu.
2007 * Return a pointer to the end of the resource.
2008 *
2009 * NOTE: flags is equivalent to the mtOption field
2010 */
2011 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
2012 {
2013 WORD flags, id = 0;
2014 HMENU hSubMenu;
2015 LPCWSTR str;
2016 BOOL end = FALSE;
2017
2018 do
2019 {
2020 flags = GET_WORD(res);
2021
2022 /* remove MF_END flag before passing it to AppendMenu()! */
2023 end = (flags & MF_END);
2024 if(end) flags ^= MF_END;
2025
2026 res += sizeof(WORD);
2027 if(!(flags & MF_POPUP))
2028 {
2029 id = GET_WORD(res);
2030 res += sizeof(WORD);
2031 }
2032 str = (LPCWSTR)res;
2033 res += (strlenW(str) + 1) * sizeof(WCHAR);
2034
2035 if (flags & MF_POPUP)
2036 {
2037 hSubMenu = CreatePopupMenu();
2038 if(!hSubMenu) return NULL;
2039 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
2040 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
2041 }
2042 else /* Not a popup */
2043 {
2044 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2045 }
2046 } while(!end);
2047 return res;
2048 }
2049
2050
2051 /**********************************************************************
2052 * MENUEX_ParseResource
2053 *
2054 * Parse an extended menu resource and add items to the menu.
2055 * Return a pointer to the end of the resource.
2056 */
2057 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2058 {
2059 WORD resinfo;
2060 do
2061 {
2062 MENUITEMINFOW mii;
2063
2064 mii.cbSize = sizeof(mii);
2065 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2066 mii.fType = GET_DWORD(res);
2067 res += sizeof(DWORD);
2068 mii.fState = GET_DWORD(res);
2069 res += sizeof(DWORD);
2070 mii.wID = GET_DWORD(res);
2071 res += sizeof(DWORD);
2072 resinfo = GET_WORD(res);
2073 res += sizeof(WORD);
2074 /* Align the text on a word boundary. */
2075 res += (~((UINT_PTR)res - 1)) & 1;
2076 mii.dwTypeData = (LPWSTR)res;
2077 mii.cch = strlenW(mii.dwTypeData);
2078 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2079 /* Align the following fields on a dword boundary. */
2080 res += (~((UINT_PTR)res - 1)) & 3;
2081
2082 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2083 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2084
2085 if (resinfo & 1) /* Pop-up? */
2086 {
2087 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2088 res += sizeof(DWORD);
2089 mii.hSubMenu = CreatePopupMenu();
2090 if (!mii.hSubMenu)
2091 {
2092 ERR("CreatePopupMenu failed\n");
2093 return NULL;
2094 }
2095
2096 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2097 {
2098 ERR("MENUEX_ParseResource failed\n");
2099 DestroyMenu(mii.hSubMenu);
2100 return NULL;
2101 }
2102 mii.fMask |= MIIM_SUBMENU;
2103 mii.fType |= MF_POPUP;
2104 }
2105 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
2106 {
2107 mii.fType |= MF_SEPARATOR;
2108 }
2109 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2110 } while (!(resinfo & MF_END));
2111 return res;
2112 }
2113
2114 NTSTATUS WINAPI
2115 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2116 {
2117 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2118 LRESULT Result = (LRESULT)hmenu;
2119 MENUINFO menuinfo = {0};
2120 MENUITEMINFOW info = {0};
2121
2122 // removing space for checkboxes from menu
2123 menuinfo.cbSize = sizeof(menuinfo);
2124 menuinfo.fMask = MIM_STYLE;
2125 GetMenuInfo(hmenu, &menuinfo);
2126 menuinfo.dwStyle |= MNS_NOCHECK;
2127 SetMenuInfo(hmenu, &menuinfo);
2128
2129 // adding bitmaps to menu items
2130 info.cbSize = sizeof(info);
2131 info.fMask |= MIIM_BITMAP;
2132 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2133 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2134 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2135 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2136 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2137 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2138 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2139 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2140
2141 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2142 }
2143
2144
2145 BOOL
2146 MenuInit(VOID)
2147 {
2148 NONCLIENTMETRICSW ncm;
2149
2150 /* get the menu font */
2151 if(!hMenuFont || !hMenuFontBold)
2152 {
2153 ncm.cbSize = sizeof(ncm);
2154 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2155 {
2156 ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2157 return FALSE;
2158 }
2159
2160 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2161 if(hMenuFont == NULL)
2162 {
2163 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2164 return FALSE;
2165 }
2166
2167 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2168 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2169 if(hMenuFontBold == NULL)
2170 {
2171 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2172 DeleteObject(hMenuFont);
2173 hMenuFont = NULL;
2174 return FALSE;
2175 }
2176 }
2177
2178 return TRUE;
2179 }
2180
2181 VOID
2182 MenuCleanup(VOID)
2183 {
2184 if (hMenuFont)
2185 {
2186 DeleteObject(hMenuFont);
2187 hMenuFont = NULL;
2188 }
2189
2190 if (hMenuFontBold)
2191 {
2192 DeleteObject(hMenuFontBold);
2193 hMenuFontBold = NULL;
2194 }
2195 }
2196
2197 /***********************************************************************
2198 * DrawMenuBarTemp (USER32.@)
2199 *
2200 * UNDOCUMENTED !!
2201 *
2202 * called by W98SE desk.cpl Control Panel Applet
2203 *
2204 * Not 100% sure about the param names, but close.
2205 *
2206 * @implemented
2207 */
2208 DWORD WINAPI
2209 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2210 {
2211 ROSMENUINFO MenuInfo;
2212 ROSMENUITEMINFO ItemInfo;
2213 UINT i;
2214 HFONT FontOld = NULL;
2215 BOOL flat_menu = FALSE;
2216
2217 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2218
2219 if (NULL == Menu)
2220 {
2221 Menu = GetMenu(Wnd);
2222 }
2223
2224 if (NULL == Font)
2225 {
2226 Font = hMenuFont;
2227 }
2228
2229 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2230 {
2231 return GetSystemMetrics(SM_CYMENU);
2232 }
2233
2234 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2235
2236 FontOld = SelectObject(DC, Font);
2237
2238 if (0 == MenuInfo.Height)
2239 {
2240 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2241 }
2242
2243 Rect->bottom = Rect->top + MenuInfo.Height;
2244
2245 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2246
2247 SelectObject(DC, GetStockObject(DC_PEN));
2248 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2249 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2250 LineTo(DC, Rect->right, Rect->bottom - 1);
2251
2252 if (0 == MenuInfo.MenuItemCount)
2253 {
2254 SelectObject(DC, FontOld);
2255 return GetSystemMetrics(SM_CYMENU);
2256 }
2257
2258 MenuInitRosMenuItemInfo(&ItemInfo);
2259 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2260 {
2261 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2262 {
2263 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2264 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2265 }
2266 }
2267 MenuCleanupRosMenuItemInfo(&ItemInfo);
2268
2269 SelectObject(DC, FontOld);
2270
2271 return MenuInfo.Height;
2272 }
2273
2274 /***********************************************************************
2275 * MenuShowSubPopup
2276 *
2277 * Display the sub-menu of the selected item of this menu.
2278 * Return the handle of the submenu, or menu if no submenu to display.
2279 */
2280 static HMENU FASTCALL
2281 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2282 {
2283 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2284 RECT Rect;
2285 ROSMENUITEMINFO ItemInfo;
2286 ROSMENUINFO SubMenuInfo;
2287 HDC Dc;
2288 HMENU Ret;
2289
2290 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2291
2292 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2293 {
2294 return MenuInfo->Self;
2295 }
2296
2297 MenuInitRosMenuItemInfo(&ItemInfo);
2298 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2299 {
2300 MenuCleanupRosMenuItemInfo(&ItemInfo);
2301 return MenuInfo->Self;
2302 }
2303 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2304 {
2305 MenuCleanupRosMenuItemInfo(&ItemInfo);
2306 return MenuInfo->Self;
2307 }
2308
2309 /* message must be sent before using item,
2310 because nearly everything may be changed by the application ! */
2311
2312 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2313 if (0 == (Flags & TPM_NONOTIFY))
2314 {
2315 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2316 MAKELPARAM(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2317 }
2318
2319 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2320 {
2321 MenuCleanupRosMenuItemInfo(&ItemInfo);
2322 return MenuInfo->Self;
2323 }
2324 Rect = ItemInfo.Rect;
2325
2326 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2327 if (0 == (ItemInfo.fState & MF_HILITE))
2328 {
2329 if (0 != (MenuInfo->Flags & MNF_POPUP))
2330 {
2331 Dc = GetDC(MenuInfo->Wnd);
2332 }
2333 else
2334 {
2335 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2336 }
2337
2338 SelectObject(Dc, hMenuFont);
2339 ItemInfo.fMask |= MIIM_STATE;
2340 ItemInfo.fState |= MF_HILITE;
2341 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2342 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2343 ! (MenuInfo->Flags & MNF_POPUP), ODA_DRAWENTIRE);
2344 ReleaseDC(MenuInfo->Wnd, Dc);
2345 }
2346
2347 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2348 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2349 {
2350 ItemInfo.Rect = Rect;
2351 }
2352
2353 ItemInfo.fMask |= MIIM_STATE;
2354 ItemInfo.fState |= MF_MOUSESELECT;
2355 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2356
2357 if (IS_SYSTEM_MENU(MenuInfo))
2358 {
2359 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2360 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2361 if (Flags & TPM_LAYOUTRTL) Rect.left;
2362 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2363 Rect.top = Rect.bottom;
2364 Rect.right = GetSystemMetrics(SM_CXSIZE);
2365 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2366 }
2367 else
2368 {
2369 GetWindowRect(MenuInfo->Wnd, &Rect);
2370 if (0 != (MenuInfo->Flags & MNF_POPUP))
2371 {
2372 if(Flags & TPM_LAYOUTRTL)
2373 Rect.left += GetSystemMetrics(SM_CXBORDER);
2374 else
2375 Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER);
2376 Rect.top += ItemInfo.Rect.top - MENU_TOP_MARGIN;//3;
2377 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2378 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
2379 - GetSystemMetrics(SM_CYBORDER);
2380 }
2381 else
2382 {
2383 if(Flags & TPM_LAYOUTRTL)
2384 Rect.left += Rect.right - ItemInfo.Rect.left;
2385 else
2386 Rect.left += ItemInfo.Rect.left;
2387 Rect.top += ItemInfo.Rect.bottom;
2388 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2389 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2390 }
2391 }
2392
2393 /* use default alignment for submenus */
2394 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2395
2396 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2397 Rect.left, Rect.top, Rect.right, Rect.bottom );
2398 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2399 {
2400 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2401 }
2402
2403 Ret = ItemInfo.hSubMenu;
2404 MenuCleanupRosMenuItemInfo(&ItemInfo);
2405
2406 return Ret;
2407 }
2408
2409 /**********************************************************************
2410 * MENU_EndMenu
2411 *
2412 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2413 *
2414 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2415 */
2416 void MENU_EndMenu( HWND hwnd )
2417 {
2418 ROSMENUINFO MenuInfo;
2419 BOOL Ret = FALSE;
2420 if (top_popup_hmenu)
2421 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2422 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2423 }
2424
2425 /***********************************************************************
2426 * MenuHideSubPopups
2427 *
2428 * Hide the sub-popup menus of this menu.
2429 */
2430 static void FASTCALL
2431 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2432 BOOL SendMenuSelect, UINT wFlags)
2433 {
2434 ROSMENUINFO SubMenuInfo;
2435 ROSMENUITEMINFO ItemInfo;
2436
2437 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2438
2439 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2440 {
2441 MenuInitRosMenuItemInfo(&ItemInfo);
2442 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2443 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2444 || 0 == (ItemInfo.fType & MF_POPUP)
2445 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2446 {
2447 MenuCleanupRosMenuItemInfo(&ItemInfo);
2448 return;
2449 }
2450 ItemInfo.fState &= ~MF_MOUSESELECT;
2451 ItemInfo.fMask |= MIIM_STATE;
2452 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2453 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2454 {
2455 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2456 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2457 DestroyWindow(SubMenuInfo.Wnd);
2458 SubMenuInfo.Wnd = NULL;
2459 MenuSetRosMenuInfo(&SubMenuInfo);
2460
2461 if (!(wFlags & TPM_NONOTIFY))
2462 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2463 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2464 }
2465 }
2466 }
2467
2468 /***********************************************************************
2469 * MenuSwitchTracking
2470 *
2471 * Helper function for menu navigation routines.
2472 */
2473 static void FASTCALL
2474 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2475 {
2476 ROSMENUINFO TopMenuInfo;
2477
2478 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2479
2480 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2481 Mt->TopMenu != PtMenuInfo->Self &&
2482 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MNF_POPUP))
2483 {
2484 /* both are top level menus (system and menu-bar) */
2485 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2486 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2487 Mt->TopMenu = PtMenuInfo->Self;
2488 }
2489 else
2490 {
2491 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2492 }
2493
2494 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2495 }
2496
2497 /***********************************************************************
2498 * MenuExecFocusedItem
2499 *
2500 * Execute a menu item (for instance when user pressed Enter).
2501 * Return the wID of the executed item. Otherwise, -1 indicating
2502 * that no menu item was executed, -2 if a popup is shown;
2503 * Have to receive the flags for the TrackPopupMenu options to avoid
2504 * sending unwanted message.
2505 *
2506 */
2507 static INT FASTCALL
2508 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2509 {
2510 ROSMENUITEMINFO ItemInfo;
2511 UINT wID;
2512
2513 TRACE("%p menu=%p\n", Mt, MenuInfo);
2514
2515 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2516 {
2517 return -1;
2518 }
2519
2520 MenuInitRosMenuItemInfo(&ItemInfo);
2521 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2522 {
2523 MenuCleanupRosMenuItemInfo(&ItemInfo);
2524 return -1;
2525 }
2526
2527 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2528
2529 if (0 == (ItemInfo.fType & MF_POPUP))
2530 {
2531 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2532 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2533 {
2534 /* If TPM_RETURNCMD is set you return the id, but
2535 do not send a message to the owner */
2536 if (0 == (Flags & TPM_RETURNCMD))
2537 {
2538 if (0 != (MenuInfo->Flags & MNF_SYSDESKMN))
2539 {
2540 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2541 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2542 }
2543 else
2544 {
2545 BOOL ret;
2546 ROSMENUINFO topmenuI;
2547 ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu);
2548 DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
2549
2550 if (dwStyle & MNS_NOTIFYBYPOS)
2551 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->FocusedItem, (LPARAM)MenuInfo->Self);
2552 else
2553 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2554 }
2555 }
2556 wID = ItemInfo.wID;
2557 MenuCleanupRosMenuItemInfo(&ItemInfo);
2558 return wID;
2559 }
2560 }
2561 else
2562 {
2563 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2564 return -2;
2565 }
2566
2567 return -1;
2568 }
2569
2570 /***********************************************************************
2571 * MenuButtonDown
2572 *
2573 * Return TRUE if we can go on with menu tracking.
2574 */
2575 static BOOL FASTCALL
2576 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2577 {
2578 int Index;
2579 ROSMENUINFO MenuInfo;
2580 ROSMENUITEMINFO Item;
2581
2582 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2583
2584 if (NULL != PtMenu)
2585 {
2586 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2587 {
2588 return FALSE;
2589 }
2590 if (IS_SYSTEM_MENU(&MenuInfo))
2591 {
2592 Index = 0;
2593 }
2594 else
2595 {
2596 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2597 }
2598 MenuInitRosMenuItemInfo(&Item);
2599 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2600 {
2601 MenuCleanupRosMenuItemInfo(&Item);
2602 return FALSE;
2603 }
2604
2605 if (!(Item.fType & MF_SEPARATOR) &&
2606 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2607 {
2608 if (MenuInfo.FocusedItem != Index)
2609 {
2610 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2611 }
2612
2613 /* If the popup menu is not already "popped" */
2614 if (0 == (Item.fState & MF_MOUSESELECT))
2615 {
2616 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2617 }
2618 }
2619
2620 MenuCleanupRosMenuItemInfo(&Item);
2621
2622 return TRUE;
2623 }
2624
2625 /* else the click was on the menu bar, finish the tracking */
2626
2627 return FALSE;
2628 }
2629
2630 /***********************************************************************
2631 * MenuButtonUp
2632 *
2633 * Return the value of MenuExecFocusedItem if
2634 * the selected item was not a popup. Else open the popup.
2635 * A -1 return value indicates that we go on with menu tracking.
2636 *
2637 */
2638 static INT FASTCALL
2639 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2640 {
2641 INT Id;
2642 ROSMENUINFO MenuInfo;
2643 ROSMENUITEMINFO ItemInfo;
2644
2645 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2646
2647 if (NULL != PtMenu)
2648 {
2649 Id = 0;
2650 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2651 {
2652 return -1;
2653 }
2654
2655 if (! IS_SYSTEM_MENU(&MenuInfo))
2656 {
2657 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2658 }
2659 MenuInitRosMenuItemInfo(&ItemInfo);
2660 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2661 MenuInfo.FocusedItem == Id)
2662 {
2663 if (0 == (ItemInfo.fType & MF_POPUP))
2664 {
2665 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2666 MenuCleanupRosMenuItemInfo(&ItemInfo);
2667 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2668 }
2669 MenuCleanupRosMenuItemInfo(&ItemInfo);
2670
2671 /* If we are dealing with the top-level menu */
2672 /* and this is a click on an already "popped" item: */
2673 /* Stop the menu tracking and close the opened submenus */
2674 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2675 {
2676 MenuCleanupRosMenuItemInfo(&ItemInfo);
2677 return 0;
2678 }
2679 }
2680 MenuCleanupRosMenuItemInfo(&ItemInfo);
2681 MenuInfo.TimeToHide = TRUE;
2682 MenuSetRosMenuInfo(&MenuInfo);
2683 }
2684
2685 return -1;
2686 }
2687
2688 /***********************************************************************
2689 * MenuPtMenu
2690 *
2691 * Walks menu chain trying to find a menu pt maps to.
2692 */
2693 static HMENU FASTCALL
2694 MenuPtMenu(HMENU Menu, POINT Pt)
2695 {
2696 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2697 ROSMENUINFO MenuInfo;
2698 ROSMENUITEMINFO ItemInfo;
2699 HMENU Ret = NULL;
2700 INT Ht;
2701
2702 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2703 {
2704 return NULL;
2705 }
2706
2707 /* try subpopup first (if any) */
2708 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2709 {
2710 MenuInitRosMenuItemInfo(&ItemInfo);
2711 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2712 0 != (ItemInfo.fType & MF_POPUP) &&
2713 0 != (ItemInfo.fState & MF_MOUSESELECT))
2714 {
2715 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2716 if (NULL != Ret)
2717 {
2718 MenuCleanupRosMenuItemInfo(&ItemInfo);
2719 return Ret;
2720 }
2721 }
2722 MenuCleanupRosMenuItemInfo(&ItemInfo);
2723 }
2724
2725 /* check the current window (avoiding WM_HITTEST) */
2726 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2727 if (0 != (MenuInfo.Flags & MNF_POPUP))
2728 {
2729 if (HTNOWHERE != Ht && HTERROR != Ht)
2730 {
2731 Ret = Menu;
2732 }
2733 }
2734 else if (HTSYSMENU == Ht)
2735 {
2736 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2737 }
2738 else if (HTMENU == Ht)
2739 {
2740 Ret = GetMenu(MenuInfo.Wnd);
2741 }
2742
2743 return Ret;
2744 }
2745
2746 /***********************************************************************
2747 * MenuMouseMove
2748 *
2749 * Return TRUE if we can go on with menu tracking.
2750 */
2751 static BOOL FASTCALL
2752 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2753 {
2754 INT Index;
2755 ROSMENUINFO MenuInfo;
2756 ROSMENUITEMINFO ItemInfo;
2757
2758 if (NULL != PtMenu)
2759 {
2760 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2761 {
2762 return TRUE;
2763 }
2764 if (IS_SYSTEM_MENU(&MenuInfo))
2765 {
2766 Index = 0;
2767 }
2768 else
2769 {
2770 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2771 }
2772 }
2773 else
2774 {
2775 Index = NO_SELECTED_ITEM;
2776 }
2777
2778 if (NO_SELECTED_ITEM == Index)
2779 {
2780 if (Mt->CurrentMenu == MenuInfo.Self ||
2781 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2782 {
2783 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2784 TRUE, Mt->TopMenu);
2785 }
2786 }
2787 else if (MenuInfo.FocusedItem != Index)
2788 {
2789 MenuInitRosMenuItemInfo(&ItemInfo);
2790 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2791 !(ItemInfo.fType & MF_SEPARATOR))
2792 {
2793 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2794 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2795 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2796 }
2797 MenuCleanupRosMenuItemInfo(&ItemInfo);
2798 }
2799
2800 return TRUE;
2801 }
2802
2803 /***********************************************************************
2804 * MenuGetSubPopup
2805 *
2806 * Return the handle of the selected sub-popup menu (if any).
2807 */
2808 static HMENU FASTCALL
2809 MenuGetSubPopup(HMENU Menu)
2810 {
2811 ROSMENUINFO MenuInfo;
2812 ROSMENUITEMINFO ItemInfo;
2813
2814 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2815 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2816 {
2817 return NULL;
2818 }
2819
2820 MenuInitRosMenuItemInfo(&ItemInfo);
2821 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2822 {
2823 MenuCleanupRosMenuItemInfo(&ItemInfo);
2824 return NULL;
2825 }
2826 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2827 {
2828 MenuCleanupRosMenuItemInfo(&ItemInfo);
2829 return ItemInfo.hSubMenu;
2830 }
2831
2832 MenuCleanupRosMenuItemInfo(&ItemInfo);
2833 return NULL;
2834 }
2835
2836 /***********************************************************************
2837 * MenuDoNextMenu
2838 *
2839 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2840 */
2841 static LRESULT FASTCALL
2842 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2843 {
2844 ROSMENUINFO TopMenuInfo;
2845 ROSMENUINFO MenuInfo;
2846
2847 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2848 {
2849 return (LRESULT) FALSE;
2850 }
2851
2852 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2853 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2854 {
2855 MDINEXTMENU NextMenu;
2856 HMENU NewMenu;
2857 HWND NewWnd;
2858 UINT Id = 0;
2859
2860 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2861 NextMenu.hmenuNext = NULL;
2862 NextMenu.hwndNext = NULL;
2863 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2864
2865 TRACE("%p [%p] -> %p [%p]\n",
2866 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2867
2868 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2869 {
2870 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2871 NewWnd = Mt->OwnerWnd;
2872 if (IS_SYSTEM_MENU(&TopMenuInfo))
2873 {
2874 /* switch to the menu bar */
2875
2876 if (0 != (Style & WS_CHILD)
2877 || NULL == (NewMenu = GetMenu(NewWnd)))
2878 {
2879 return FALSE;
2880 }
2881
2882 if (VK_LEFT == Vk)
2883 {
2884 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2885 {
2886 return FALSE;
2887 }
2888 Id = MenuInfo.MenuItemCount - 1;
2889 }
2890 }
2891 else if (0 != (Style & WS_SYSMENU))
2892 {
2893 /* switch to the system menu */
2894 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2895 }
2896 else
2897 {
2898 return FALSE;
2899 }
2900 }
2901 else /* application returned a new menu to switch to */
2902 {
2903 NewMenu = NextMenu.hmenuNext;
2904 NewWnd = NextMenu.hwndNext;
2905
2906 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2907 {
2908 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2909
2910 if (0 != (Style & WS_SYSMENU)
2911 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2912 {
2913 /* get the real system menu */
2914 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2915 }
2916 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2917 {
2918 /* FIXME: Not sure what to do here;
2919 * perhaps try to track NewMenu as a popup? */
2920
2921 WARN(" -- got confused.\n");
2922 return FALSE;
2923 }
2924 }
2925 else
2926 {
2927 return FALSE;
2928 }
2929 }
2930
2931 if (NewMenu != Mt->TopMenu)
2932 {
2933 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2934 FALSE, 0 );
2935 if (Mt->CurrentMenu != Mt->TopMenu)
2936 {
2937 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2938 }
2939 }
2940
2941 if (NewWnd != Mt->OwnerWnd)
2942 {
2943 Mt->OwnerWnd = NewWnd;
2944 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
2945 SetCapture(Mt->OwnerWnd); // 2
2946 }
2947
2948 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2949 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2950 {
2951 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2952 }
2953
2954 return TRUE;
2955 }
2956
2957 return FALSE;
2958 }
2959
2960 /***********************************************************************
2961 * MenuSuspendPopup
2962 *
2963 * The idea is not to show the popup if the next input message is
2964 * going to hide it anyway.
2965 */
2966 static BOOL FASTCALL
2967 MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
2968 {
2969 MSG msg;
2970
2971 msg.hwnd = Mt->OwnerWnd;
2972
2973 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
2974 // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
2975
2976 switch( uMsg )
2977 {
2978 case WM_KEYDOWN:
2979 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2980 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2981 {
2982 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2983 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2984 if( msg.message == WM_KEYDOWN &&
2985 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2986 {
2987 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2988 return TRUE;
2989 }
2990 }
2991 break;
2992 }
2993 /* failures go through this */
2994 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2995 return FALSE;
2996 }
2997
2998 /***********************************************************************
2999 * MenuKeyEscape
3000 *
3001 * Handle a VK_ESCAPE key event in a menu.
3002 */
3003 static BOOL FASTCALL
3004 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3005 {
3006 BOOL EndMenu = TRUE;
3007 ROSMENUINFO MenuInfo;
3008 HMENU MenuTmp, MenuPrev;
3009
3010 if (Mt->CurrentMenu != Mt->TopMenu)
3011 {
3012 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3013 && 0 != (MenuInfo.Flags & MNF_POPUP))
3014 {
3015 MenuPrev = MenuTmp = Mt->TopMenu;
3016
3017 /* close topmost popup */
3018 while (MenuTmp != Mt->CurrentMenu)
3019 {
3020 MenuPrev = MenuTmp;
3021 MenuTmp = MenuGetSubPopup(MenuPrev);
3022 }
3023
3024 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3025 {
3026 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
3027 }
3028 Mt->CurrentMenu = MenuPrev;
3029 EndMenu = FALSE;
3030 }
3031 }
3032
3033 return EndMenu;
3034 }
3035
3036 /***********************************************************************
3037 * MenuKeyLeft
3038 *
3039 * Handle a VK_LEFT key event in a menu.
3040 */
3041 static void FASTCALL
3042 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3043 {
3044 ROSMENUINFO MenuInfo;
3045 ROSMENUINFO TopMenuInfo;
3046 ROSMENUINFO PrevMenuInfo;
3047 HMENU MenuTmp, MenuPrev;
3048 UINT PrevCol;
3049
3050 MenuPrev = MenuTmp = Mt->TopMenu;
3051
3052 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3053 {
3054 return;
3055 }
3056
3057 /* Try to move 1 column left (if possible) */
3058 if ( (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)) != NO_SELECTED_ITEM)
3059 {
3060 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3061 {
3062 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3063 }
3064 return;
3065 }
3066
3067 /* close topmost popup */
3068 while (MenuTmp != Mt->CurrentMenu)
3069 {
3070 MenuPrev = MenuTmp;
3071 MenuTmp = MenuGetSubPopup(MenuPrev);
3072 }
3073
3074 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3075 {
3076 return;
3077 }
3078 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3079 Mt->CurrentMenu = MenuPrev;
3080
3081 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3082 {
3083 return;
3084 }
3085 if ((MenuPrev == Mt->TopMenu) && !(TopMenuInfo.Flags & MNF_POPUP))
3086 {
3087 /* move menu bar selection if no more popups are left */
3088
3089 if (!MenuDoNextMenu(Mt, VK_LEFT, Flags))
3090 {
3091 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3092 }
3093
3094 if (MenuPrev != MenuTmp || Mt->TrackFlags & TF_SUSPENDPOPUP)
3095 {
3096 /* A sublevel menu was displayed - display the next one
3097 * unless there is another displacement coming up */
3098
3099 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3100 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3101 {
3102 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3103 TRUE, Flags);
3104 }
3105 }
3106 }
3107 }
3108
3109 /***********************************************************************
3110 * MenuKeyRight
3111 *
3112 * Handle a VK_RIGHT key event in a menu.
3113 */
3114 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3115 {
3116 HMENU hmenutmp;
3117 ROSMENUINFO MenuInfo;
3118 ROSMENUINFO CurrentMenuInfo;
3119 UINT NextCol;
3120
3121 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3122 Mt->CurrentMenu, Mt->TopMenu);
3123
3124 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3125 if ((MenuInfo.Flags & MNF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3126 {
3127 /* If already displaying a popup, try to display sub-popup */
3128
3129 hmenutmp = Mt->CurrentMenu;
3130 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3131 {
3132 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3133 }
3134
3135 /* if subpopup was displayed then we are done */
3136 if (hmenutmp != Mt->CurrentMenu) return;
3137 }
3138
3139 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3140 {
3141 return;
3142 }
3143
3144 /* Check to see if there's another column */
3145 if ( (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)) != NO_SELECTED_ITEM)
3146 {
3147 TRACE("Going to %d.\n", NextCol);
3148 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3149 {
3150 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3151 }
3152 return;
3153 }
3154
3155 if (!(MenuInfo.Flags & MNF_POPUP)) /* menu bar tracking */
3156 {
3157 if (Mt->CurrentMenu != Mt->TopMenu)
3158 {
3159 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3160 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3161 }
3162 else
3163 {
3164 hmenutmp = NULL;
3165 }
3166
3167 /* try to move to the next item */
3168 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3169 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3170
3171 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3172 {
3173 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3174 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3175 {
3176 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3177 TRUE, Flags);
3178 }
3179 }
3180 }
3181 }
3182
3183 /***********************************************************************
3184 * MenuTrackMenu
3185 *
3186 * Menu tracking code.
3187 */
3188 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3189 HWND hwnd, const RECT *lprect )
3190 {
3191 MSG msg;
3192 ROSMENUINFO MenuInfo;
3193 ROSMENUITEMINFO ItemInfo;
3194 BOOL fRemove;
3195 INT executedMenuId = -1;
3196 MTRACKER mt;
3197 HWND capture_win;
3198 BOOL enterIdleSent = FALSE;
3199
3200 mt.TrackFlags = 0;
3201 mt.CurrentMenu = hmenu;
3202 mt.TopMenu = hmenu;
3203 mt.OwnerWnd = hwnd;
3204 mt.Pt.x = x;
3205 mt.Pt.y = y;
3206
3207 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3208 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3209 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3210
3211 if (!IsMenu(hmenu))
3212 {
3213 WARN("Invalid menu handle %p\n", hmenu);
3214 SetLastError( ERROR_INVALID_MENU_HANDLE );
3215 return FALSE;
3216 }
3217
3218 fEndMenu = FALSE;
3219 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3220 {
3221 return FALSE;
3222 }
3223
3224 if (wFlags & TPM_BUTTONDOWN)
3225 {
3226 /* Get the result in order to start the tracking or not */
3227 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3228 fEndMenu = !fRemove;
3229 }
3230
3231 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3232
3233 /* owner may not be visible when tracking a popup, so use the menu itself */
3234 capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd;
3235 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1
3236 SetCapture(capture_win); // 2
3237
3238 while (! fEndMenu)
3239 {
3240 BOOL ErrorExit = FALSE;
3241 PVOID menu = ValidateHandle(mt.CurrentMenu, TYPE_MENU);
3242 if (!menu) /* sometimes happens if I do a window manager close */
3243 break;
3244
3245 /* we have to keep the message in the queue until it's
3246 * clear that menu loop is not over yet. */
3247
3248 for (;;)
3249 {
3250 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3251 {
3252 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3253 /* remove the message from the queue */
3254 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3255 }
3256 else
3257 {
3258 /* ReactOS Check */
3259 if (!ValidateHwnd(mt.OwnerWnd) || !ValidateHwnd(MenuInfo.Wnd))
3260 {
3261 ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works.
3262 break;
3263 }
3264 if (!enterIdleSent)
3265 {
3266 HWND win = MenuInfo.Flags & MNF_POPUP ? MenuInfo.Wnd : NULL;
3267 enterIdleSent = TRUE;
3268 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3269 }
3270 WaitMessage();
3271 }
3272 }
3273
3274 if (ErrorExit) break; // Gracefully dropout.
3275
3276 /* check if EndMenu() tried to cancel us, by posting this message */
3277 if (msg.message == WM_CANCELMODE)
3278 {
3279 /* we are now out of the loop */
3280 fEndMenu = TRUE;
3281
3282 /* remove the message from the queue */
3283 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3284
3285 /* break out of internal loop, ala ESCAPE */
3286 break;
3287 }
3288
3289 TranslateMessage( &msg );
3290 mt.Pt = msg.pt;
3291
3292 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3293 enterIdleSent=FALSE;
3294
3295 fRemove = FALSE;
3296 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3297 {
3298 /*
3299 * Use the mouse coordinates in lParam instead of those in the MSG
3300 * struct to properly handle synthetic messages. They are already
3301 * in screen coordinates.
3302 */
3303 mt.Pt.x = (short)LOWORD(msg.lParam);
3304 mt.Pt.y = (short)HIWORD(msg.lParam);
3305
3306 /* Find a menu for this mouse event */
3307 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
3308
3309 switch(msg.message)
3310 {
3311 /* no WM_NC... messages in captured state */
3312
3313 case WM_RBUTTONDBLCLK:
3314 case WM_RBUTTONDOWN:
3315 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3316 /* fall through */
3317 case WM_LBUTTONDBLCLK:
3318 case WM_LBUTTONDOWN:
3319 /* If the message belongs to the menu, removes it from the queue */
3320 /* Else, end menu tracking */
3321 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
3322 fEndMenu = !fRemove;
3323 break;
3324
3325 case WM_RBUTTONUP:
3326 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3327 /* fall through */
3328 case WM_LBUTTONUP:
3329 /* Check if a menu was selected by the mouse */
3330 if (hmenu)
3331 {
3332 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
3333 TRACE("executedMenuId %d\n", executedMenuId);
3334
3335 /* End the loop if executedMenuId is an item ID */
3336 /* or if the job was done (executedMenuId = 0). */
3337 fEndMenu = fRemove = (executedMenuId != -1);
3338 }
3339 /* No menu was selected by the mouse */
3340 /* if the function was called by TrackPopupMenu, continue
3341 with the menu tracking. If not, stop it */
3342 else
3343 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3344
3345 break;
3346
3347 case WM_MOUSEMOVE:
3348 /* the selected menu item must be changed every time */
3349 /* the mouse moves. */
3350
3351 if (hmenu)
3352 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3353
3354 } /* switch(msg.message) - mouse */
3355 }
3356 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3357 {
3358 fRemove = TRUE; /* Keyboard messages are always removed */
3359 switch(msg.message)
3360 {
3361 case WM_KEYDOWN:
3362 case WM_SYSKEYDOWN:
3363 switch(msg.wParam)
3364 {
3365 case VK_MENU:
3366 case VK_F10:
3367 fEndMenu = TRUE;
3368 break;
3369
3370 case VK_HOME:
3371 case VK_END:
3372 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3373 {
3374 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
3375 NO_SELECTED_ITEM, FALSE, 0 );
3376 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3377 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3378 }
3379 break;
3380
3381 case VK_UP:
3382 case VK_DOWN: /* If on menu bar, pull-down the menu */
3383 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3384 {
3385 if (!(MenuInfo.Flags & MNF_POPUP))
3386 {
3387 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3388 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3389 }
3390 else /* otherwise try to move selection */
3391 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3392 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3393 }
3394 break;
3395
3396 case VK_LEFT:
3397 MenuKeyLeft( &mt, wFlags );
3398 break;
3399
3400 case VK_RIGHT:
3401 MenuKeyRight( &mt, wFlags );
3402 break;
3403
3404 case VK_ESCAPE:
3405 fEndMenu = MenuKeyEscape(&mt, wFlags);
3406 break;
3407
3408 case VK_F1:
3409 {
3410 HELPINFO hi;
3411 hi.cbSize = sizeof(HELPINFO);
3412 hi.iContextType = HELPINFO_MENUITEM;
3413 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3414 {
3415 if (MenuInfo.FocusedItem == NO_SELECTED_ITEM)
3416 hi.iCtrlId = 0;
3417 else
3418 {
3419 MenuInitRosMenuItemInfo(&ItemInfo);
3420 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3421 MenuInfo.FocusedItem,
3422 &ItemInfo))
3423 {
3424 hi.iCtrlId = ItemInfo.wID;
3425 }
3426 else
3427 {
3428 hi.iCtrlId = 0;
3429 }
3430 MenuCleanupRosMenuItemInfo(&ItemInfo);
3431 }
3432 }
3433 hi.hItemHandle = hmenu;
3434 hi.dwContextId = MenuInfo.dwContextHelpID;
3435 hi.MousePos = msg.pt;
3436 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3437 break;
3438 }
3439
3440 default:
3441 break;
3442 }
3443 break; /* WM_KEYDOWN */
3444
3445 case WM_CHAR:
3446 case WM_SYSCHAR:
3447 {
3448 UINT pos;
3449
3450 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3451 if (msg.wParam == L'\r' || msg.wParam == L' ')
3452 {
3453 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3454 fEndMenu = (executedMenuId != -2);
3455 break;
3456 }
3457
3458 /* Hack to avoid control chars. */
3459 /* We will find a better way real soon... */
3460 if (msg.wParam < 32) break;
3461
3462 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
3463 LOWORD(msg.wParam), FALSE);
3464 if (pos == (UINT)-2) fEndMenu = TRUE;
3465 else if (pos == (UINT)-1) MessageBeep(0);
3466 else
3467 {
3468 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
3469 TRUE, 0);
3470 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3471 fEndMenu = (executedMenuId != -2);
3472 }
3473 }
3474 break;
3475 } /* switch(msg.message) - kbd */
3476 }
3477 else
3478 {
3479 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3480 DispatchMessageW( &msg );
3481 continue;
3482 }
3483
3484 if (!fEndMenu) fRemove = TRUE;
3485
3486 /* finally remove message from the queue */
3487
3488 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3489 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3490 else mt.TrackFlags &= ~TF_SKIPREMOVE;
3491 }
3492
3493 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3494 SetCapture(NULL); /* release the capture */
3495
3496 /* If dropdown is still painted and the close box is clicked on
3497 then the menu will be destroyed as part of the DispatchMessage above.
3498 This will then invalidate the menu handle in mt.hTopMenu. We should
3499 check for this first. */
3500 if( IsMenu( mt.TopMenu ) )
3501 {
3502 if (IsWindow(mt.OwnerWnd))
3503 {
3504 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3505 {
3506 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
3507
3508 if (MenuInfo.Flags & MNF_POPUP)
3509 {
3510 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
3511 DestroyWindow(MenuInfo.Wnd);
3512 MenuInfo.Wnd = NULL;
3513
3514 if (!(wFlags & TPM_NONOTIFY))
3515 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
3516 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
3517 }
3518 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3519 }
3520
3521 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
3522 }
3523
3524 /* Reset the variable for hiding menu */
3525 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3526 {
3527 MenuInfo.TimeToHide = FALSE;
3528 MenuSetRosMenuInfo(&MenuInfo);
3529 }
3530 }
3531
3532 /* The return value is only used by TrackPopupMenu */
3533 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3534 if (executedMenuId == -1) executedMenuId = 0;
3535 return executedMenuId;
3536 }
3537
3538 /***********************************************************************
3539 * MenuInitTracking
3540 */
3541 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3542 {
3543 ROSMENUINFO MenuInfo;
3544
3545 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3546
3547 HideCaret(0);
3548
3549 /* This makes the menus of applications built with Delphi work.
3550 * It also enables menus to be displayed in more than one window,
3551 * but there are some bugs left that need to be fixed in this case.
3552 */
3553 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
3554 {
3555 MenuInfo.Wnd = hWnd;
3556 MenuSetRosMenuInfo(&MenuInfo);
3557 }
3558
3559 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3560 if (!(wFlags & TPM_NONOTIFY))
3561 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3562
3563 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3564
3565 if (!(wFlags & TPM_NONOTIFY))
3566 {
3567 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3568 /* If an app changed/recreated menu bar entries in WM_INITMENU
3569 * menu sizes will be recalculated once the menu created/shown.
3570 */
3571
3572 if (!MenuInfo.Height)
3573 {
3574 /* app changed/recreated menu bar entries in WM_INITMENU
3575 Recalculate menu sizes else clicks will not work */
3576 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3577 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3578
3579 }
3580 }
3581
3582 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
3583 hWnd,
3584 MenuInfo.Flags & MNF_SYSDESKMN ? OBJID_SYSMENU : OBJID_MENU,
3585 CHILDID_SELF, 0);
3586 return TRUE;
3587 }
3588 /***********************************************************************
3589 * MenuExitTracking
3590 */
3591 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3592 {
3593 TRACE("hwnd=%p\n", hWnd);
3594
3595 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
3596 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3597 ShowCaret(0);
3598 top_popup = 0;
3599 top_popup_hmenu = NULL;
3600 return TRUE;
3601 }
3602
3603 /***********************************************************************
3604 * MenuTrackMouseMenuBar
3605 *
3606 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3607 */
3608 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3609 {
3610 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3611 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3612
3613 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3614
3615 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3616 if (IsMenu(hMenu))
3617 {
3618 /* map point to parent client coordinates */
3619 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3620 if (Parent != GetDesktopWindow())
3621 {
3622 ScreenToClient(Parent, &pt);
3623 }
3624
3625 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3626 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3627 MenuExitTracking(hWnd, FALSE);
3628 }
3629 }
3630
3631
3632 /***********************************************************************
3633 * MenuTrackKbdMenuBar
3634 *
3635 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3636 */
3637 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3638 {
3639 UINT uItem = NO_SELECTED_ITEM;
3640 HMENU hTrackMenu;
3641 ROSMENUINFO MenuInfo;
3642 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3643
3644 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3645
3646 /* find window that has a menu */
3647
3648 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
3649 (WS_CHILD | WS_POPUP)) != WS_CHILD))
3650 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3651
3652 /* check if we have to track a system menu */
3653
3654 hTrackMenu = GetMenu( hwnd );
3655 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3656 {
3657 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3658 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
3659 uItem = 0;
3660 wParam |= HTSYSMENU; /* prevent item lookup */
3661 }
3662
3663 if (!IsMenu( hTrackMenu )) return;
3664
3665 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3666
3667 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
3668 {
3669 goto track_menu;
3670 }
3671
3672 if( wChar && wChar != ' ' )
3673 {
3674 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
3675 if ( uItem >= (UINT)(-2) )
3676 {
3677 if( uItem == (UINT)(-1) ) MessageBeep(0);
3678 /* schedule end of menu tracking */
3679 wFlags |= TF_ENDMENU;
3680 goto track_menu;
3681 }
3682 }
3683
3684 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
3685
3686 if (!(wParam & HTSYSMENU) || wChar == ' ')
3687 {
3688 if( uItem == NO_SELECTED_ITEM )
3689 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
3690 else
3691 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3692 }
3693
3694 track_menu:
3695 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3696 MenuExitTracking( hwnd, FALSE );
3697 }
3698
3699 /**********************************************************************
3700 * TrackPopupMenuEx (USER32.@)
3701 */
3702 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
3703 HWND Wnd, LPTPMPARAMS Tpm)
3704 {
3705 BOOL ret = FALSE;
3706 ROSMENUINFO MenuInfo;
3707
3708 if (!IsMenu(Menu))
3709 {
3710 SetLastError( ERROR_INVALID_MENU_HANDLE );
3711 return FALSE;
3712 }
3713
3714 /* ReactOS Check */
3715 if (!ValidateHwnd(Wnd))
3716 {
3717 /* invalid window see wine menu.c test_menu_trackpopupmenu line 3146 */
3718 return FALSE;
3719 }
3720
3721 MenuGetRosMenuInfo(&MenuInfo, Menu);
3722 if (IsWindow(MenuInfo.Wnd))
3723 {
3724 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3725 return FALSE;
3726 }
3727
3728 MenuInitTracking(Wnd, Menu, TRUE, Flags);
3729
3730 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3731 if (!(Flags & TPM_NONOTIFY))
3732 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
3733
3734 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
3735 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
3736 Tpm ? &Tpm->rcExclude : NULL);
3737 MenuExitTracking(Wnd, TRUE);
3738 return ret;
3739 }
3740
3741 /**********************************************************************
3742 * TrackPopupMenu (USER32.@)
3743 */
3744 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
3745 int Reserved, HWND Wnd, CONST RECT *Rect)
3746 {
3747 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
3748 }
3749
3750 /*
3751 * From MSDN:
3752 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3753 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3754 *
3755 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3756 *
3757 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3758 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3759 * MFT_STRING is replaced by MIIM_STRING.
3760 * (So, I guess we should use MIIM_STRING only for strings?)
3761 *
3762 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3763 *
3764 * Based on wine, SetMenuItemInfo_common:
3765 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3766 * it will result in a error.
3767 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3768 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3769 *
3770 */
3771 static
3772 BOOL
3773 FASTCALL
3774 MenuSetItemData(
3775 LPMENUITEMINFOW mii,
3776 UINT Flags,
3777 UINT_PTR IDNewItem,
3778 LPCWSTR NewItem,
3779 BOOL Unicode)
3780 {
3781 /*
3782 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3783 */
3784 if(Flags & MF_BITMAP)
3785 {
3786 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
3787 mii->hbmpItem = (HBITMAP) NewItem;
3788
3789 if (Flags & MF_HELP)
3790 {
3791 /* increase ident */
3792 mii->fType |= MF_HELP;
3793 }
3794 }
3795 else if(Flags & MF_OWNERDRAW)
3796 {
3797 mii->fType |= MFT_OWNERDRAW;
3798 mii->fMask |= MIIM_DATA;
3799 mii->dwItemData = (DWORD_PTR) NewItem;
3800 }
3801 else if (Flags & MF_SEPARATOR)
3802 {
3803 mii->fType |= MFT_SEPARATOR;
3804 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3805 Flags |= MF_GRAYED|MF_DISABLED;
3806 }
3807 else /* Default action MF_STRING. */
3808 {
3809 /* Item beginning with a backspace is a help item */
3810 if (NewItem != NULL)
3811 {
3812 if (Unicode)
3813 {
3814 if (*NewItem == '\b')
3815 {
3816 mii->fType |= MF_HELP;
3817 NewItem++;
3818 }
3819 }
3820 else
3821 {
3822 LPCSTR NewItemA = (LPCSTR) NewItem;
3823 if (*NewItemA == '\b')
3824 {
3825 mii->fType |= MF_HELP;
3826 NewItemA++;
3827 NewItem = (LPCWSTR) NewItemA;
3828 }
3829 }
3830
3831 if (Flags & MF_HELP)
3832 mii->fType |= MF_HELP;
3833 mii->fMask |= MIIM_STRING;
3834 mii->fType |= MFT_STRING; /* Zero */
3835 mii->dwTypeData = (LPWSTR)NewItem;
3836 if (Unicode)
3837 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
3838 else
3839 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
3840 }
3841 else
3842 {
3843 mii->fType |= MFT_SEPARATOR;
3844 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3845 Flags |= MF_GRAYED|MF_DISABLED;
3846 }
3847 }
3848
3849 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
3850 {
3851 mii->fType |= MFT_RIGHTJUSTIFY;
3852 }
3853
3854 if(Flags & MF_MENUBREAK)
3855 {
3856 mii->fType |= MFT_MENUBREAK;
3857 }
3858 else if(Flags & MF_MENUBARBREAK)
3859 {
3860 mii->fType |= MFT_MENUBARBREAK;
3861 }
3862
3863 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
3864 {
3865 if (Flags & MF_GRAYED)
3866 mii->fState |= MF_GRAYED;
3867
3868 if (Flags & MF_DISABLED)
3869 mii->fState |= MF_DISABLED;
3870
3871 mii->fMask |= MIIM_STATE;
3872 }
3873 else if (Flags & MF_HILITE)
3874 {
3875 mii->fState |= MF_HILITE;
3876 mii->fMask |= MIIM_STATE;
3877 }
3878 else /* default state */
3879 {
3880 mii->fState |= MFS_ENABLED;
3881 mii->fMask |= MIIM_STATE;
3882 }
3883
3884 if(Flags & MF_POPUP)
3885 {
3886 mii->fType |= MF_POPUP;
3887 mii->fMask |= MIIM_SUBMENU;
3888 mii->hSubMenu = (HMENU)IDNewItem;
3889 }
3890 else
3891 {
3892 mii->fMask |= MIIM_ID;
3893 mii->wID = (UINT)IDNewItem;
3894 }
3895 return TRUE;
3896 }
3897
3898 NTSTATUS WINAPI
3899 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
3900 {
3901 PLOADMENU_CALLBACK_ARGUMENTS Common;
3902 LRESULT Result;
3903
3904 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
3905
3906 Result = (LRESULT)LoadMenuW( Common->hModule,
3907 IS_INTRESOURCE(Common->MenuName[0]) ?
3908 MAKEINTRESOURCE(Common->MenuName[0]) :
3909 (LPCWSTR)&Common->MenuName);
3910
3911 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3912 }
3913
3914
3915 /* FUNCTIONS *****************************************************************/
3916
3917 /*static BOOL
3918 MenuIsStringItem(ULONG TypeData)
3919 {
3920 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3921 }*/
3922
3923
3924 /*
3925 * @implemented
3926 */
3927 BOOL WINAPI
3928 AppendMenuA(HMENU hMenu,
3929 UINT uFlags,
3930 UINT_PTR uIDNewItem,
3931 LPCSTR lpNewItem)
3932 {
3933 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3934 lpNewItem));
3935 }
3936
3937
3938 /*
3939 * @implemented
3940 */
3941 BOOL WINAPI
3942 AppendMenuW(HMENU hMenu,
3943 UINT uFlags,
3944 UINT_PTR uIDNewItem,
3945 LPCWSTR lpNewItem)
3946 {
3947 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3948 lpNewItem));
3949 }
3950
3951
3952 /*
3953 * @implemented
3954 */
3955 DWORD WINAPI
3956 CheckMenuItem(HMENU hmenu,
3957 UINT uIDCheckItem,
3958 UINT uCheck)
3959 {
3960 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3961 }
3962
3963 static
3964 BOOL
3965 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
3966 {
3967 UINT ItemCount, i;
3968 PROSMENUITEMINFO Items = NULL;
3969 UINT cChecked, cUnchecked;
3970 BOOL bRet = TRUE;
3971 //ROSMENUINFO mi;
3972
3973 if(idFirst > idLast)
3974 return FALSE;
3975
3976 ItemCount = GetMenuItemCount(hMenu);
3977
3978 //mi.cbSize = sizeof(ROSMENUINFO);
3979 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3980
3981
3982 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
3983 {
3984 ERR("MenuGetAllRosMenuItemInfo failed\n");
3985 return FALSE;
3986 }
3987
3988 cChecked = cUnchecked = 0;
3989
3990 for (i = 0 ; i < ItemCount; i++)
3991 {
3992 BOOL check = FALSE;
3993 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
3994 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
3995
3996 if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
3997 {
3998 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
3999 continue;
4000 }
4001 if (uFlags & MF_BYPOSITION)
4002 {
4003 if (i < idFirst || i > idLast)
4004 continue;
4005
4006 if (i == idCheck)
4007 {
4008 cChecked++;
4009 check = TRUE;
4010 }
4011 else
4012 {
4013 cUnchecked++;
4014 }
4015 }
4016 else
4017 {
4018 if (Items[i].wID < idFirst || Items[i].wID > idLast)
4019 continue;
4020
4021 if (Items[i].wID == idCheck)
4022 {
4023 cChecked++;
4024 check = TRUE;
4025 }
4026 else
4027 {
4028 cUnchecked++;
4029 }
4030 }
4031
4032 if (!bCheck)
4033 continue;
4034
4035 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
4036 if (check)
4037 {
4038 Items[i].fType |= MFT_RADIOCHECK;
4039 Items[i].fState |= MFS_CHECKED;
4040 }
4041 else
4042 {
4043 Items[i].fState &= ~MFS_CHECKED;
4044 }
4045
4046 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
4047 {
4048 ERR("MenuSetRosMenuItemInfo failed\n");
4049 bRet = FALSE;
4050 break;
4051 }
4052 }
4053 HeapFree(GetProcessHeap(), 0, Items);
4054
4055 *pChecked += cChecked;
4056 *pUnchecked += cUnchecked;
4057
4058 if (cChecked || cUnchecked)
4059 (*pMenuChanged)++;
4060
4061 return bRet;
4062 }
4063
4064 /*
4065 * @implemented
4066 */
4067 BOOL WINAPI
4068 CheckMenuRadioItem(HMENU hmenu,
4069 UINT idFirst,
4070 UINT idLast,
4071 UINT idCheck,
4072 UINT uFlags)
4073 {
4074 UINT cChecked = 0;
4075 UINT cUnchecked = 0;
4076 UINT cMenuChanged = 0;
4077
4078 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4079 return FALSE;
4080
4081 if (cMenuChanged > 1)
4082 return FALSE;
4083
4084 cMenuChanged = 0;
4085 cChecked = 0;
4086 cUnchecked = 0;
4087
4088 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4089 return FALSE;
4090
4091 return (cChecked != 0);
4092 }
4093
4094
4095 /*
4096 * @implemented
4097 */
4098 HMENU WINAPI
4099 CreateMenu(VOID)
4100 {
4101 MenuLoadBitmaps();
4102 return NtUserxCreateMenu();
4103 }
4104
4105
4106 /*
4107 * @implemented
4108 */
4109 HMENU WINAPI
4110 CreatePopupMenu(VOID)
4111 {
4112 MenuLoadBitmaps();
4113 return NtUserxCreatePopupMenu();
4114 }
4115
4116
4117 /*
4118 * @implemented
4119 */
4120 BOOL WINAPI
4121 DrawMenuBar(HWND hWnd)
4122 {
4123 // return NtUserxDrawMenuBar(hWnd);
4124 ROSMENUINFO MenuInfo;
4125 HMENU hMenu;
4126 hMenu = GetMenu(hWnd);
4127 if (!hMenu)
4128 return FALSE;
4129 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4130 MenuInfo.Height = 0; // make sure to recalc size
4131 MenuSetRosMenuInfo(&MenuInfo);
4132
4133 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4134 SWP_NOZORDER | SWP_FRAMECHANGED );
4135 return TRUE;
4136 }
4137
4138 /*
4139 * @implemented
4140 */
4141 BOOL WINAPI
4142 EnableMenuItem(HMENU hMenu,
4143 UINT uIDEnableItem,
4144 UINT uEnable)
4145 {
4146 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4147 }
4148
4149 /*
4150 * @implemented
4151 */
4152 BOOL WINAPI
4153 EndMenu(VOID)
4154 {
4155 GUITHREADINFO guii;
4156 guii.cbSize = sizeof(GUITHREADINFO);
4157 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4158 {
4159 if (!fEndMenu &&
4160 top_popup &&
4161 guii.hwndMenuOwner != top_popup )
4162 {
4163 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4164 }
4165 }
4166
4167 /* if we are in the menu code, and it is active */
4168 if (!fEndMenu && top_popup)
4169 {
4170 /* terminate the menu handling code */
4171 fEndMenu = TRUE;
4172
4173 /* needs to be posted to wakeup the internal menu handler */
4174 /* which will now terminate the menu, in the event that */
4175 /* the main window was minimized, or lost focus, so we */
4176 /* don't end up with an orphaned menu */
4177 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4178 }
4179 return TRUE;
4180 }
4181
4182 // So this one maybe one day it will be a callback!
4183 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4184 UINT wHilite )
4185 {
4186 ROSMENUINFO MenuInfo;
4187 ROSMENUITEMINFO mii;
4188 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4189 if (!hWnd)
4190 {
4191 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
4192 return FALSE;
4193 }
4194 if (!NtUserMenuItemInfo(hMenu, wItemID, wHilite, &mii, FALSE)) return FALSE;
4195 if (!NtUserMenuInfo(hMenu, &MenuInfo, FALSE)) return FALSE;
4196 if (MenuInfo.FocusedItem == wItemID) return TRUE;
4197 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4198 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4199 return TRUE;
4200 }
4201
4202 /*
4203 * @implemented
4204 */
4205 HMENU WINAPI
4206 GetMenu(HWND hWnd)
4207 {
4208 PWND Wnd = ValidateHwnd(hWnd);
4209
4210 if (!Wnd)
4211 return NULL;
4212
4213 return UlongToHandle(Wnd->IDMenu);
4214 }
4215
4216
4217 /*
4218 * @implemented
4219 */
4220 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4221 {
4222 BOOL Ret;
4223 Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
4224 // Reason to move to server side!!!!!
4225 if (!Ret) return Ret;
4226 // EL HAXZO!!!
4227 pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
4228 if (!idItem)
4229 {
4230 pmbi->fFocused = pmbi->fBarFocused;
4231 }
4232
4233 return TRUE;
4234 }
4235
4236
4237 /*
4238 * @implemented
4239 */
4240 LONG WINAPI
4241 GetMenuCheckMarkDimensions(VOID)
4242 {
4243 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4244 GetSystemMetrics(SM_CYMENUCHECK)));
4245 }
4246
4247
4248 /*
4249 * @implemented
4250 */
4251 UINT WINAPI
4252 GetMenuDefaultItem(HMENU hMenu,
4253 UINT fByPos,
4254 UINT gmdiFlags)
4255 {
4256 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4257 }
4258
4259
4260 /*
4261 * @implemented
4262 */
4263 BOOL WINAPI
4264 GetMenuInfo(HMENU hmenu,
4265 LPMENUINFO lpcmi)
4266 {
4267 ROSMENUINFO mi;
4268 BOOL res = FALSE;
4269
4270 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4271 return FALSE;
4272
4273 RtlZeroMemory(&mi, sizeof(MENUINFO));
4274 mi.cbSize = sizeof(MENUINFO);
4275 mi.fMask = lpcmi->fMask;
4276
4277 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4278
4279 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4280 return res;
4281 }
4282
4283
4284 /*
4285 * @implemented
4286 */
4287 int WINAPI
4288 GetMenuItemCount(HMENU Menu)
4289 {
4290 ROSMENUINFO MenuInfo;
4291
4292 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4293 }
4294
4295
4296 /*
4297 * @implemented
4298 */
4299 UINT WINAPI
4300 GetMenuItemID(HMENU hMenu,
4301 int nPos)
4302 {
4303 ROSMENUITEMINFO mii;
4304
4305 mii.cbSize = sizeof(MENUITEMINFOW);
4306 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4307
4308 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4309 {
4310 return -1;
4311 }
4312
4313 if (NULL != mii.hSubMenu)
4314 {
4315 return -1;
4316 }
4317 if (0 == mii.wID)
4318 {
4319 return -1;
4320 }
4321
4322 return mii.wID;
4323 }
4324
4325
4326 /*
4327 * @implemented
4328 */
4329 BOOL WINAPI
4330 GetMenuItemInfoA(
4331 HMENU Menu,
4332 UINT Item,
4333 BOOL ByPosition,
4334 LPMENUITEMINFOA mii)
4335 {
4336 MENUITEMINFOW miiW;
4337 LPSTR AnsiBuffer;
4338 INT Count;
4339
4340 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4341 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4342 {
4343 SetLastError(ERROR_INVALID_PARAMETER);
4344 return FALSE;
4345 }
4346
4347 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4348 {
4349 /* No text requested, just pass on */
4350 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4351 }
4352
4353 AnsiBuffer = mii->dwTypeData;
4354 Count = miiW.cch = mii->cch;
4355 RtlCopyMemory(&miiW, mii, mii->cbSize);
4356 miiW.dwTypeData = 0;
4357
4358 if (AnsiBuffer)
4359 {
4360 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4361 miiW.cch * sizeof(WCHAR));
4362 if (miiW.dwTypeData == NULL) return FALSE;
4363 miiW.dwTypeData[0] = 0;
4364 }
4365
4366 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4367 {
4368 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4369 return FALSE;
4370 }
4371
4372 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4373
4374 if (!AnsiBuffer || !Count)
4375 {
4376 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4377 mii->dwTypeData = AnsiBuffer;
4378 mii->cch = miiW.cch;
4379 return TRUE;
4380 }
4381
4382 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4383 {
4384 if (miiW.cch)
4385 {
4386 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4387 {
4388 AnsiBuffer[0] = 0;
4389 }
4390 if (Count > miiW.cch)
4391 {
4392 AnsiBuffer[miiW.cch] = 0;
4393 }
4394 mii->cch = miiW.cch;
4395 }
4396 }
4397 else
4398 {
4399 AnsiBuffer[0] = 0;
4400 }
4401
4402 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4403 mii->dwTypeData = AnsiBuffer;
4404
4405 return TRUE;
4406 }
4407
4408
4409 /*
4410 * @implemented
4411 */
4412 BOOL WINAPI
4413 GetMenuItemInfoW(
4414 HMENU Menu,
4415 UINT Item,
4416 BOOL ByPosition,
4417 LPMENUITEMINFOW mii)
4418 {
4419 MENUITEMINFOW miiW;
4420 LPWSTR String;
4421 INT Count;
4422
4423 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4424 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4425 {
4426 SetLastError(ERROR_INVALID_PARAMETER);
4427 return FALSE;
4428 }
4429
4430 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4431 {
4432 /* No text requested, just pass on */
4433 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4434 }
4435
4436 String = mii->dwTypeData;
4437 Count = mii->cch;
4438 RtlCopyMemory(&miiW, mii, mii->cbSize);
4439 miiW.dwTypeData = 0;
4440
4441 if (String)
4442 {
4443 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4444 miiW.cch * sizeof(WCHAR));
4445 if (miiW.dwTypeData == NULL) return FALSE;
4446 miiW.dwTypeData[0] = 0;
4447 }
4448
4449 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4450 {
4451 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4452 return FALSE;
4453 }
4454
4455 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4456
4457 if (!String || !Count)
4458 {
4459 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4460 mii->dwTypeData = String; // may not be zero.
4461 mii->cch = miiW.cch;
4462 return TRUE;
4463 }
4464
4465 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4466 {
4467 lstrcpynW( String, miiW.dwTypeData, Count );
4468 }
4469
4470 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4471 mii->dwTypeData = String;
4472 mii->cch = strlenW(String);
4473 return TRUE;
4474 }
4475
4476
4477 /*
4478 * @implemented
4479 */
4480 UINT
4481 WINAPI
4482 GetMenuState(
4483 HMENU hMenu,
4484 UINT uId,
4485 UINT uFlags)
4486 {
4487 ROSMENUINFO MenuInfo;
4488 ROSMENUITEMINFO mii;
4489 memset( &mii, 0, sizeof(mii) );
4490 mii.cbSize = sizeof(MENUITEMINFOW);
4491 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4492
4493 SetLastError(0);
4494 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4495 {
4496 UINT nSubItems = 0;
4497 if(mii.hSubMenu)
4498 {
4499 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4500 {
4501 return (UINT) -1;
4502 }
4503 nSubItems = MenuInfo.MenuItemCount;
4504
4505 /* FIXME - ported from wine, does that work (0xff)? */
4506 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4507 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4508
4509 return (UINT)-1; /* Invalid submenu */
4510 }
4511
4512 /* FIXME - ported from wine, does that work? */
4513 return (mii.fType | mii.fState);
4514 }
4515
4516 return (UINT)-1;
4517 }
4518
4519
4520 /*
4521 * @implemented
4522 */
4523 int
4524 WINAPI
4525 GetMenuStringA(
4526 HMENU hMenu,
4527 UINT uIDItem,
4528 LPSTR lpString,
4529 int nMaxCount,
4530 UINT uFlag)
4531 {
4532 MENUITEMINFOA mii;
4533 memset( &mii, 0, sizeof(mii) );
4534 mii.dwTypeData = lpString;
4535 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4536 mii.fType = MFT_STRING;
4537 mii.cbSize = sizeof(MENUITEMINFOA);
4538 mii.cch = nMaxCount;
4539
4540 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4541 return 0;
4542 else
4543 return mii.cch;
4544 }
4545
4546
4547 /*
4548 * @implemented
4549 */
4550 int
4551 WINAPI
4552 GetMenuStringW(
4553 HMENU hMenu,
4554 UINT uIDItem,
4555 LPWSTR lpString,
4556 int nMaxCount,
4557 UINT uFlag)
4558 {
4559 MENUITEMINFOW miiW;
4560 memset( &miiW, 0, sizeof(miiW) );
4561 miiW.dwTypeData = lpString;
4562 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4563 miiW.fType = MFT_STRING;
4564 miiW.cbSize = sizeof(MENUITEMINFOW);
4565 miiW.cch = nMaxCount;
4566
4567 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4568 return 0;
4569 else
4570 return miiW.cch;
4571 }
4572
4573
4574 /*
4575 * @implemented
4576 */
4577 HMENU
4578 WINAPI
4579 GetSubMenu(
4580 HMENU hMenu,
4581 int nPos)
4582 {
4583 ROSMENUITEMINFO mi;
4584
4585 mi.cbSize = sizeof(MENUITEMINFOW);
4586 mi.fMask = MIIM_SUBMENU;
4587
4588 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4589 {
4590 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4591 }
4592
4593 return NULL;
4594 }
4595
4596 /*
4597 * @implemented
4598 */
4599 HMENU
4600 WINAPI
4601 GetSystemMenu(
4602 HWND hWnd,
4603 BOOL bRevert)
4604 {
4605 HMENU TopMenu;
4606
4607 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4608
4609 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4610 }
4611
4612
4613 /*
4614 * @implemented
4615 */
4616 BOOL
4617 WINAPI
4618 InsertMenuA(
4619 HMENU hMenu,
4620 UINT uPosition,
4621 UINT uFlags,
4622 UINT_PTR uIDNewItem,
4623 LPCSTR lpNewItem)
4624 {
4625 MENUITEMINFOA mii;
4626 memset( &mii, 0, sizeof(mii) );
4627 mii.cbSize = sizeof(MENUITEMINFOA);
4628 mii.fMask = MIIM_FTYPE;
4629
4630 MenuSetItemData((LPMENUITEMINFOW) &mii,
4631 uFlags,
4632 uIDNewItem,
4633 (LPCWSTR) lpNewItem,
4634 FALSE);
4635
4636 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4637 }
4638
4639
4640
4641 /*
4642 * @implemented
4643 */
4644 BOOL
4645 WINAPI
4646 InsertMenuItemA(
4647 HMENU hMenu,
4648 UINT uItem,
4649 BOOL fByPosition,
4650 LPCMENUITEMINFOA lpmii)
4651 {
4652 MENUITEMINFOW mi;
4653 UNICODE_STRING MenuText;
4654 BOOL res = FALSE;
4655 BOOL CleanHeap = FALSE;
4656
4657 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4658 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4659 {
4660 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4661
4662 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4663 {
4664 mi.cbSize = sizeof( MENUITEMINFOW);
4665 mi.hbmpItem = NULL;
4666 }
4667 /* copy the text string */
4668 if (((mi.fMask & MIIM_STRING) ||
4669 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4670 && mi.dwTypeData != NULL)
4671 {
4672 if (!RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData))
4673 {
4674 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
4675 return FALSE;
4676 }
4677 mi.dwTypeData = MenuText.Buffer;
4678 mi.cch = MenuText.Length / sizeof(WCHAR);
4679 CleanHeap = TRUE;
4680 }
4681 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4682
4683 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4684 }
4685 return res;
4686 }
4687
4688
4689 /*
4690 * @implemented
4691 */
4692 BOOL
4693 WINAPI
4694 InsertMenuItemW(
4695 HMENU hMenu,
4696 UINT uItem,
4697 BOOL fByPosition,
4698 LPCMENUITEMINFOW lpmii)
4699 {
4700 MENUITEMINFOW mi;
4701 UNICODE_STRING MenuText;
4702 BOOL res = FALSE;
4703
4704 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4705 if a bad user passes bad data, we crash his process instead of the
4706 entire kernel */
4707
4708 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4709 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4710 {
4711 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4712
4713 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4714 {
4715 mi.cbSize = sizeof( MENUITEMINFOW);
4716 mi.hbmpItem = NULL;
4717 }
4718 /* copy the text string */
4719 if (((mi.fMask & MIIM_STRING) ||
4720 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4721 && mi.dwTypeData != NULL)
4722 {
4723 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4724 mi.dwTypeData = MenuText.Buffer;
4725 mi.cch = MenuText.Length / sizeof(WCHAR);
4726 }
4727 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4728 }
4729 return res;
4730 }
4731
4732
4733 /*
4734 * @implemented
4735 */
4736 BOOL
4737 WINAPI
4738 InsertMenuW(
4739 HMENU hMenu,
4740 UINT uPosition,
4741 UINT uFlags,
4742 UINT_PTR uIDNewItem,
4743 LPCWSTR lpNewItem)
4744 {
4745 MENUITEMINFOW mii;
4746 memset( &mii, 0, sizeof(mii) );
4747 mii.cbSize = sizeof(MENUITEMINFOW);
4748 mii.fMask = MIIM_FTYPE;
4749
4750 MenuSetItemData( &mii,
4751 uFlags,
4752 uIDNewItem,
4753 lpNewItem,
4754 TRUE);
4755
4756 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4757 }
4758
4759
4760 /*
4761 * @implemented
4762 */
4763 BOOL
4764 WINAPI
4765 IsMenu(
4766 HMENU Menu)
4767 {
4768 if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
4769 return FALSE;
4770 }
4771
4772
4773 /*
4774 * @implemented
4775 */
4776 HMENU WINAPI
4777 LoadMenuA(HINSTANCE hInstance,
4778 LPCSTR lpMenuName)
4779 {
4780 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4781 if (Resource == NULL)
4782 {
4783 return(NULL);
4784 }
4785 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4786 }
4787
4788
4789 /*
4790 * @implemented
4791 */
4792 HMENU WINAPI
4793 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4794 {
4795 return(LoadMenuIndirectW(lpMenuTemplate));
4796 }
4797
4798
4799 /*
4800 * @implemented
4801 */
4802 HMENU WINAPI
4803 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4804 {
4805 HMENU hMenu;
4806 WORD version, offset;
4807 LPCSTR p = (LPCSTR)lpMenuTemplate;
4808
4809 version = GET_WORD(p);
4810 p += sizeof(WORD);
4811
4812 switch (version)
4813 {
4814 case 0: /* standard format is version of 0 */
4815 offset = GET_WORD(p);
4816 p += sizeof(WORD) + offset;
4817 if (!(hMenu = CreateMenu())) return 0;
4818 if (!MENU_ParseResource(p, hMenu))
4819 {
4820 DestroyMenu(hMenu);
4821 return 0;
4822 }
4823 return hMenu;
4824 case 1: /* extended format is version of 1 */
4825 offset = GET_WORD(p);
4826 p += sizeof(WORD) + offset;
4827 if (!(hMenu = CreateMenu())) return 0;
4828 if (!MENUEX_ParseResource(p, hMenu))
4829 {
4830 DestroyMenu( hMenu );
4831 return 0;
4832 }
4833 return hMenu;
4834 default:
4835 ERR("Menu template version %d not supported.\n", version);
4836 return 0;
4837 }
4838 }
4839
4840
4841 /*
4842 * @implemented
4843 */
4844 HMENU WINAPI
4845 LoadMenuW(HINSTANCE hInstance,
4846 LPCWSTR lpMenuName)
4847 {
4848 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4849 if (Resource == NULL)
4850 {
4851 return(NULL);
4852 }
4853 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4854 }
4855
4856
4857 /*
4858 * @implemented
4859 */
4860 int
4861 WINAPI
4862 MenuItemFromPoint(
4863 HWND hWnd,
4864 HMENU hMenu,
4865 POINT ptScreen)
4866 {
4867 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4868 }
4869
4870
4871 /*
4872 * @implemented
4873 */
4874 BOOL
4875 WINAPI
4876 ModifyMenuA(
4877 HMENU hMnu,
4878 UINT uPosition,
4879 UINT uFlags,
4880 UINT_PTR uIDNewItem,
4881 LPCSTR lpNewItem)
4882 {
4883 ROSMENUINFO mi;
4884 ROSMENUITEMINFO rmii;
4885 MENUITEMINFOA mii;
4886 memset( &mii, 0, sizeof(mii) );
4887 mii.cbSize = sizeof(MENUITEMINFOA);
4888 mii.fMask = MIIM_FTYPE;
4889
4890 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4891
4892 mi.Height = 0;
4893
4894 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4895
4896 MenuInitRosMenuItemInfo( &rmii );
4897
4898 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4899
4900 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4901 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4902
4903 MenuCleanupRosMenuItemInfo( &rmii );
4904
4905 MenuSetItemData((LPMENUITEMINFOW) &mii,
4906 uFlags,
4907 uIDNewItem,
4908 (LPCWSTR) lpNewItem,
4909 FALSE);
4910
4911 return SetMenuItemInfoA( hMnu,
4912 uPosition,
4913 (BOOL)(MF_BYPOSITION & uFlags),
4914 &mii);
4915 }
4916
4917
4918 /*
4919 * @implemented
4920 */
4921 BOOL
4922 WINAPI
4923 ModifyMenuW(
4924 HMENU hMnu,
4925 UINT uPosition,
4926 UINT uFlags,
4927 UINT_PTR uIDNewItem,
4928 LPCWSTR lpNewItem)
4929 {
4930 ROSMENUINFO mi;
4931 ROSMENUITEMINFO rmii;
4932 MENUITEMINFOW mii;
4933 memset ( &mii, 0, sizeof(mii) );
4934 mii.cbSize = sizeof(MENUITEMINFOW);
4935 mii.fMask = MIIM_FTYPE;
4936
4937 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4938
4939 mi.Height = 0; // Force size recalculation.
4940
4941 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4942
4943 MenuInitRosMenuItemInfo( &rmii );
4944
4945 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4946
4947 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4948 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4949
4950 MenuCleanupRosMenuItemInfo( &rmii );
4951
4952 /* Init new data for this menu item */
4953 MenuSetItemData( &mii,
4954 uFlags,
4955 uIDNewItem,
4956 lpNewItem,
4957 TRUE);
4958
4959 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4960 return SetMenuItemInfoW( hMnu,
4961 uPosition,
4962 (BOOL)(MF_BYPOSITION & uFlags),
4963 &mii);
4964 }
4965
4966
4967 /*
4968 * @implemented
4969 */
4970 BOOL WINAPI
4971 SetMenu(HWND hWnd,
4972 HMENU hMenu)
4973 {
4974 return NtUserSetMenu(hWnd, hMenu, TRUE);
4975 }
4976
4977
4978 /*
4979 * @implemented
4980 */
4981 BOOL
4982 WINAPI
4983 SetMenuInfo(
4984 HMENU hmenu,
4985 LPCMENUINFO lpcmi)
4986 {
4987 ROSMENUINFO mi;
4988 BOOL res = FALSE;
4989
4990 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4991 {
4992 SetLastError(ERROR_INVALID_PARAMETER);
4993 return res;
4994 }
4995
4996 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4997 return NtUserMenuInfo(hmenu, &mi, TRUE);
4998 }
4999
5000
5001 /*
5002 * @implemented
5003 */
5004 BOOL
5005 WINAPI
5006 SetMenuItemBitmaps(
5007 HMENU hMenu,
5008 UINT uPosition,
5009 UINT uFlags,
5010 HBITMAP hBitmapUnchecked,
5011 HBITMAP hBitmapChecked)
5012 {
5013 ROSMENUITEMINFO uItem;
5014 memset ( &uItem, 0, sizeof(uItem) );
5015 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
5016
5017 if(!(NtUserMenuItemInfo(hMenu, uPosition,
5018 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
5019
5020 if (!hBitmapChecked && !hBitmapUnchecked)
5021 {
5022 uItem.fState &= ~MF_USECHECKBITMAPS;
5023 }
5024 else /* Install new bitmaps */
5025 {
5026 uItem.hbmpChecked = hBitmapChecked;
5027 uItem.hbmpUnchecked = hBitmapUnchecked;
5028 uItem.fState |= MF_USECHECKBITMAPS;
5029 }
5030 return NtUserMenuItemInfo(hMenu, uPosition,
5031 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
5032 }
5033
5034
5035 /*
5036 * @implemented
5037 */
5038 BOOL
5039 WINAPI
5040 SetMenuItemInfoA(
5041 HMENU hMenu,
5042 UINT uItem,
5043 BOOL fByPosition,
5044 LPCMENUITEMINFOA lpmii)
5045 {
5046 MENUITEMINFOW MenuItemInfoW;
5047 UNICODE_STRING UnicodeString;
5048 ULONG Result = FALSE;
5049
5050 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5051
5052 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5053 {
5054 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5055 MenuItemInfoW.hbmpItem = NULL;
5056 }
5057 /*
5058 * MIIM_STRING == good
5059 * MIIM_TYPE & MFT_STRING == good
5060 * MIIM_STRING & MFT_STRING == good
5061 * MIIM_STRING & MFT_OWNERSRAW == good
5062 */
5063 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5064 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5065 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5066 && MenuItemInfoW.dwTypeData != NULL)
5067 {
5068 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5069 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
5070 (LPSTR)MenuItemInfoW.dwTypeData))
5071 {
5072 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
5073 return FALSE;
5074 }
5075 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
5076 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
5077 }
5078 else
5079 {
5080 UnicodeString.Buffer = NULL;
5081 }
5082
5083 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5084 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5085
5086 if (UnicodeString.Buffer != NULL)
5087 {
5088 RtlFreeUnicodeString(&UnicodeString);
5089 }
5090
5091 return Result;
5092 }
5093
5094
5095 /*
5096 * @implemented
5097 */
5098 BOOL
5099 WINAPI
5100 SetMenuItemInfoW(
5101 HMENU hMenu,
5102 UINT uItem,
5103 BOOL fByPosition,
5104 LPCMENUITEMINFOW lpmii)
5105 {
5106 MENUITEMINFOW MenuItemInfoW;
5107 ULONG Result;
5108
5109 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5110
5111 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5112 {
5113 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5114 MenuItemInfoW.hbmpItem = NULL;
5115 }
5116
5117 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5118 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5119 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5120 && MenuItemInfoW.dwTypeData != NULL)
5121 {
5122 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5123 }
5124 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5125 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5126
5127 return Result;
5128 }
5129
5130 /*
5131 * @implemented
5132 */
5133 BOOL
5134 WINAPI
5135 SetSystemMenu (
5136 HWND hwnd,
5137 HMENU hMenu)
5138 {
5139 if(!hwnd)
5140 {
5141 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5142 return FALSE;
5143 }
5144 if(!hMenu)
5145 {
5146 SetLastError(ERROR_INVALID_MENU_HANDLE);
5147 return FALSE;
5148 }
5149 return NtUserSetSystemMenu(hwnd, hMenu);
5150 }
5151
5152 //
5153 // Example for the Win32/User32 rewrite.
5154 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5155 //
5156 //
5157 BOOL
5158 WINAPI
5159 NEWTrackPopupMenu(
5160 HMENU Menu,
5161 UINT Flags,
5162 int x,
5163 int y,
5164 int Reserved,
5165 HWND Wnd,
5166 CONST RECT *Rect)
5167 {
5168 return NtUserTrackPopupMenuEx( Menu,
5169 Flags,
5170 x,
5171 y,
5172 Wnd,
5173 NULL); // LPTPMPARAMS is null
5174 }
5175
5176
5177 /*
5178 * @implemented
5179 */
5180 DWORD
5181 WINAPI
5182 GetMenuContextHelpId(HMENU hmenu)
5183 {
5184 ROSMENUINFO mi;
5185 mi.cbSize = sizeof(ROSMENUINFO);
5186 mi.fMask = MIM_HELPID;
5187
5188 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5189 {
5190 return mi.dwContextHelpID;
5191 }
5192 return 0;
5193 }
5194
5195 /*
5196 * @unimplemented
5197 */
5198 BOOL
5199 WINAPI
5200 MenuWindowProcA(
5201 HWND hWnd,
5202 ULONG_PTR Result,
5203 UINT Msg,
5204 WPARAM wParam,
5205 LPARAM lParam
5206 )
5207 {
5208 if ( Msg < WM_USER)
5209 {
5210 LRESULT lResult;
5211 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5212 if (Result)
5213 {
5214 Result = (ULONG_PTR)lResult;
5215 return TRUE;
5216 }
5217 return FALSE;
5218 }
5219 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5220
5221 }
5222
5223 /*
5224 * @unimplemented
5225 */
5226 BOOL
5227 WINAPI
5228 MenuWindowProcW(
5229 HWND hWnd,
5230 ULONG_PTR Result,
5231 UINT Msg,
5232 WPARAM wParam,
5233 LPARAM lParam
5234 )
5235 {
5236 if ( Msg < WM_USER)
5237 {
5238 LRESULT lResult;
5239 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5240 if (Result)
5241 {
5242 Result = (ULONG_PTR)lResult;
5243 return TRUE;
5244 }
5245 return FALSE;
5246 }
5247 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5248 }
5249
5250 /*
5251 * @implemented
5252 */
5253 BOOL
5254 WINAPI
5255 ChangeMenuW(
5256 HMENU hMenu,
5257 UINT cmd,
5258 LPCWSTR lpszNewItem,
5259 UINT cmdInsert,
5260 UINT flags)
5261 {
5262 /*
5263 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5264 for MF_DELETE. We should check the parameters for all others
5265 MF_* actions also (anybody got a doc on ChangeMenu?).
5266 */
5267
5268 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5269 {
5270 case MF_APPEND :
5271 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5272
5273 case MF_DELETE :
5274 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5275
5276 case MF_CHANGE :
5277 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5278
5279 case MF_REMOVE :
5280 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5281 flags &~ MF_REMOVE);
5282
5283 default : /* MF_INSERT */
5284 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5285 };
5286 }
5287
5288 /*
5289 * @implemented
5290 */
5291 BOOL
5292 WINAPI
5293 ChangeMenuA(
5294 HMENU hMenu,
5295 UINT cmd,
5296 LPCSTR lpszNewItem,
5297 UINT cmdInsert,
5298 UINT flags)
5299 {
5300 /*
5301 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5302 for MF_DELETE. We should check the parameters for all others
5303 MF_* actions also (anybody got a doc on ChangeMenu?).
5304 */
5305
5306 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5307 {
5308 case MF_APPEND :
5309 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5310
5311 case MF_DELETE :
5312 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5313
5314 case MF_CHANGE :
5315 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5316
5317 case MF_REMOVE :
5318 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5319 flags &~ MF_REMOVE);
5320
5321 default : /* MF_INSERT */
5322 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5323 };
5324 }
5325
5326
5327
5328
5329