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