3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS user32.dll
5 * FILE: user32/windows/menu.c
8 * PROGRAMMERS: Casper S. Hornstrup
12 /* INCLUDES ******************************************************************/
16 #include <wine/debug.h>
17 WINE_DEFAULT_DEBUG_CHANNEL(user32
);
19 /* internal popup menu window messages */
20 #define MM_SETMENUHANDLE (WM_USER + 0)
21 #define MM_GETMENUHANDLE (WM_USER + 1)
23 /* Internal MenuTrackMenu() flags */
24 #define TPM_INTERNAL 0xF0000000
25 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
26 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
27 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
29 /* TYPES *********************************************************************/
31 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
33 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
34 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
35 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
37 #define IS_SYSTEM_MENU(MenuInfo) \
38 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
40 #define IS_SYSTEM_POPUP(MenuInfo) \
41 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
43 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
44 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
46 #define MENU_ITEM_HBMP_SPACE (5)
47 #define MENU_BAR_ITEMS_SPACE (12)
48 #define SEPARATOR_HEIGHT (5)
49 #define MENU_TAB_SPACE (8)
54 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
55 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
56 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
57 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
59 /* internal flags for menu tracking */
61 #define TF_ENDMENU 0x0001
62 #define TF_SUSPENDPOPUP 0x0002
63 #define TF_SKIPREMOVE 0x0004
68 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
69 HMENU TopMenu
; /* initial menu */
70 HWND OwnerWnd
; /* where notifications are sent */
74 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
76 /*********************************************************************
77 * PopupMenu class descriptor
79 const struct builtin_class_descr POPUPMENU_builtin_class
=
81 POPUPMENU_CLASS_ATOMW
, /* name */
82 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
83 (WNDPROC
) NULL
, /* FIXME - procA */
84 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
85 sizeof(MENUINFO
*), /* extra */
86 (LPCWSTR
) IDC_ARROW
, /* cursor */
87 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
91 /* INTERNAL FUNCTIONS ********************************************************/
93 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
94 * Of course I didnt copy the ASM code because we want this to be portable
95 * and it needs to go away.
99 #define GET_WORD(ptr) (*(WORD *)(ptr))
102 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
105 HFONT hMenuFont
= NULL
;
106 HFONT hMenuFontBold
= NULL
;
108 /* Flag set by EndMenu() to force an exit from menu tracking */
109 static BOOL fEndMenu
= FALSE
;
111 /* Use global popup window because there's no way 2 menus can
112 * be tracked at the same time. */
113 static HWND TopPopup
;
115 /* Dimension of the menu bitmaps */
116 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
118 static HBITMAP StdMnArrow
= NULL
;
119 static HBITMAP BmpSysMenu
= NULL
;
121 static SIZE MenuCharSize
;
123 /***********************************************************************
126 * Get full information about menu
129 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
131 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
132 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
134 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
137 /***********************************************************************
140 * Set full information about menu
143 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
145 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
146 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
148 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
151 /***********************************************************************
152 * MenuInitRosMenuItemInfo
154 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
157 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
159 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
160 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
163 /***********************************************************************
164 * MenuGetRosMenuItemInfo
166 * Get full information about a menu item
169 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
171 UINT Save_Mask
= ItemInfo
->fMask
; /* Save the org mask bits. */
173 if (ItemInfo
->dwTypeData
!= NULL
)
175 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
179 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
180 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
181 ItemInfo
->dwTypeData
= NULL
;
183 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
189 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
)
192 ItemInfo
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0,
193 ItemInfo
->cch
* sizeof(WCHAR
));
194 if (NULL
== ItemInfo
->dwTypeData
)
199 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
204 ItemInfo
->dwTypeData
[ItemInfo
->cch
- 1] = UNICODE_NULL
;
206 ItemInfo
->fMask
= Save_Mask
;
210 /***********************************************************************
211 * MenuSetRosMenuItemInfo
213 * Set selected information about a menu item, need to set the mask bits.
216 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
220 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
&&
221 ItemInfo
->dwTypeData
!= NULL
)
223 ItemInfo
->cch
= strlenW(ItemInfo
->dwTypeData
);
225 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
230 /***********************************************************************
231 * MenuCleanupRosMenuItemInfo
233 * Cleanup after use of MenuGet/SetRosMenuItemInfo
236 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
238 if (ItemInfo
->dwTypeData
!= NULL
)
240 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
241 ItemInfo
->dwTypeData
= NULL
;
245 /***********************************************************************
246 * MenuGetAllRosMenuItemInfo
248 * Get full information about all menu items
251 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
255 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
256 if (BufSize
== (DWORD
) -1 || BufSize
== 0)
260 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
261 if (NULL
== *ItemInfo
)
266 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
269 /***********************************************************************
270 * MenuCleanupAllRosMenuItemInfo
272 * Cleanup after use of MenuGetAllRosMenuItemInfo
275 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
277 HeapFree(GetProcessHeap(), 0, ItemInfo
);
281 /***********************************************************************
284 * Load the arrow bitmap. We can't do this from MenuInit since user32
285 * can also be used (and thus initialized) from text-mode.
288 MenuLoadBitmaps(VOID
)
290 /* Load menu bitmaps */
291 if (NULL
== StdMnArrow
)
293 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
295 if (NULL
!= StdMnArrow
)
298 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
299 ArrowBitmapWidth
= bm
.bmWidth
;
300 ArrowBitmapHeight
= bm
.bmHeight
;
304 /* Load system buttons bitmaps */
305 if (NULL
== BmpSysMenu
)
307 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
311 /***********************************************************************
312 * MenuGetBitmapItemSize
314 * Get the size of a bitmap item.
317 MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem
, SIZE
*Size
, HWND WndOwner
)
320 HBITMAP Bmp
= lpitem
->hbmpItem
;
322 Size
->cx
= Size
->cy
= 0;
324 /* check if there is a magic menu item associated with this item */
325 if (0 != Bmp
&& IS_MAGIC_ITEM((INT
)(Bmp
)))
327 switch((INT_PTR
) Bmp
)
329 case (INT_PTR
)HBMMENU_CALLBACK
:
331 MEASUREITEMSTRUCT measItem
;
332 measItem
.CtlType
= ODT_MENU
;
334 measItem
.itemID
= lpitem
->wID
;
335 measItem
.itemWidth
= lpitem
->Rect
.right
- lpitem
->Rect
.left
;
336 measItem
.itemHeight
= lpitem
->Rect
.bottom
- lpitem
->Rect
.top
;
337 measItem
.itemData
= lpitem
->dwItemData
;
338 SendMessageW( WndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
339 Size
->cx
= measItem
.itemWidth
;
340 Size
->cy
= measItem
.itemHeight
;
345 case (INT_PTR
) HBMMENU_SYSTEM
:
346 if (0 != lpitem
->dwItemData
)
348 Bmp
= (HBITMAP
) lpitem
->dwItemData
;
352 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
353 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
354 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
355 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
356 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
357 /* FIXME: Why we need to subtract these magic values? */
358 /* to make them smaller than the menu bar? */
359 Size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
360 Size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
362 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
363 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
364 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
365 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
367 FIXME("Magic menu bitmap not implemented\n");
372 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
374 Size
->cx
= Bm
.bmWidth
;
375 Size
->cy
= Bm
.bmHeight
;
379 /***********************************************************************
382 * Draw a bitmap item.
385 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
,
386 HMENU hmenu
, HWND WndOwner
, UINT odaction
, BOOL MenuBar
)
392 int w
= Rect
->right
- Rect
->left
;
393 int h
= Rect
->bottom
- Rect
->top
;
396 HBITMAP hbmpToDraw
= (HBITMAP
) Item
->hbmpItem
;
399 /* Check if there is a magic menu item associated with this item */
400 if (IS_MAGIC_ITEM(hbmpToDraw
))
406 switch ((INT_PTR
)hbmpToDraw
)
408 case (INT_PTR
) HBMMENU_SYSTEM
:
409 if (NULL
!= Item
->dwTypeData
)
411 Bmp
= (HBITMAP
)Item
->dwTypeData
;
412 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
419 if (!BmpSysMenu
) BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
421 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
425 /* only use right half of the bitmap */
426 BmpXoffset
= Bm
.bmWidth
/ 2;
427 Bm
.bmWidth
-= BmpXoffset
;
430 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
431 Flags
= DFCS_CAPTIONRESTORE
;
433 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
435 Flags
= DFCS_CAPTIONMIN
;
437 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
439 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
441 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
442 Flags
= DFCS_CAPTIONCLOSE
;
444 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
445 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
447 case (INT_PTR
) HBMMENU_CALLBACK
:
449 DRAWITEMSTRUCT drawItem
;
451 drawItem
.CtlType
= ODT_MENU
;
453 drawItem
.itemID
= Item
->wID
;
454 drawItem
.itemAction
= odaction
;
455 drawItem
.itemState
= (Item
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
456 drawItem
.itemState
|= (Item
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
457 drawItem
.itemState
|= (Item
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
458 drawItem
.itemState
|= (Item
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
459 drawItem
.itemState
|= (Item
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
460 drawItem
.hwndItem
= (HWND
)hmenu
;
462 drawItem
.rcItem
= *Rect
;
463 drawItem
.itemData
= Item
->dwItemData
;
464 /* some applications make this assumption on the DC's origin */
465 SetViewportOrgEx( Dc
, Item
->Rect
.left
, Item
->Rect
.top
, &origorg
);
466 OffsetRect( &drawItem
.rcItem
, - Item
->Rect
.left
, - Item
->Rect
.top
);
467 SendMessageW( WndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
468 SetViewportOrgEx( Dc
, origorg
.x
, origorg
.y
, NULL
);
473 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
474 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
475 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
476 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
478 FIXME("Magic menu bitmap not implemented\n");
481 InflateRect(&r
, -1, -1);
482 if (0 != (Item
->fState
& MF_HILITE
))
484 Flags
|= DFCS_PUSHED
;
486 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
490 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
496 DcMem
= CreateCompatibleDC(Dc
);
497 SelectObject(DcMem
, Bmp
);
499 /* handle fontsize > bitmap_height */
500 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
502 Rop
= ((Item
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmpToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
503 if ((Item
->fState
& MF_HILITE
) && Item
->hbmpItem
)
505 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
507 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
511 /***********************************************************************
514 * Draw a single menu item.
517 MenuDrawMenuItem(HWND hWnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
518 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
522 BOOL flat_menu
= FALSE
;
524 PWINDOW Wnd
= ValidateHwnd(hWnd
);
529 if (0 != (Item
->fType
& MF_SYSMENU
))
531 if ( (Wnd
->Style
& WS_MINIMIZE
))
533 UserGetInsideRectNC(Wnd
, &Rect
);
534 UserDrawSysMenuButton(hWnd
, Dc
, &Rect
,
535 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
540 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
541 bkgnd
= (MenuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
545 if (0 != (Item
->fState
& MF_HILITE
))
547 if (MenuBar
&& !flat_menu
)
549 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
550 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
554 if (0 != (Item
->fState
& MF_GRAYED
))
556 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
560 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
562 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
567 if (0 != (Item
->fState
& MF_GRAYED
))
569 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
573 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
575 SetBkColor(Dc
, GetSysColor(bkgnd
));
580 if (Item
->fType
& MF_OWNERDRAW
)
583 ** Experimentation under Windows reveals that an owner-drawn
584 ** menu is given the rectangle which includes the space it requested
585 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
586 ** and a popup-menu arrow. This is the value of lpitem->rect.
587 ** Windows will leave all drawing to the application except for
588 ** the popup-menu arrow. Windows always draws that itself, after
589 ** the menu owner has finished drawing.
593 dis
.CtlType
= ODT_MENU
;
595 dis
.itemID
= Item
->wID
;
596 dis
.itemData
= (DWORD
)Item
->dwItemData
;
598 if (0 != (Item
->fState
& MF_CHECKED
))
600 dis
.itemState
|= ODS_CHECKED
;
602 if (0 != (Item
->fState
& MF_GRAYED
))
604 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
606 if (0 != (Item
->fState
& MF_HILITE
))
608 dis
.itemState
|= ODS_SELECTED
;
610 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
611 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
614 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
615 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd
,
616 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
617 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
619 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
620 /* Draw the popup-menu arrow */
621 if (0 != (Item
->fType
& MF_POPUP
))
623 HDC DcMem
= CreateCompatibleDC(Dc
);
626 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
627 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
628 ((Rect
.top
+ Rect
.bottom
) - ArrowBitmapHeight
) / 2,
629 ArrowBitmapWidth
, ArrowBitmapHeight
,
630 DcMem
, 0, 0, SRCCOPY
);
631 SelectObject(DcMem
, OrigBitmap
);
637 TRACE("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
638 Item
->Rect
.right
, Item
->Rect
.bottom
);
640 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
645 if (Item
->fState
& MF_HILITE
)
649 InflateRect (&Rect
, -1, -1);
650 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
651 InflateRect (&Rect
, 1, 1);
652 FrameRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
658 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
662 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
668 FillRect(Dc
, &Rect
, GetSysColorBrush(bkgnd
));
671 SetBkMode(Dc
, TRANSPARENT
);
673 /* vertical separator */
674 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
680 rc
.bottom
= Height
- 3;
683 oldPen
= SelectObject( Dc
, GetSysColorPen(COLOR_BTNSHADOW
) );
684 MoveToEx( Dc
, rc
.left
, rc
.top
, NULL
);
685 LineTo( Dc
, rc
.left
, rc
.bottom
);
686 SelectObject( Dc
, oldPen
);
689 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
692 /* horizontal separator */
693 if (0 != (Item
->fType
& MF_SEPARATOR
))
699 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
702 oldPen
= SelectObject( Dc
, GetSysColorPen(COLOR_BTNSHADOW
) );
703 MoveToEx( Dc
, rc
.left
, rc
.top
, NULL
);
704 LineTo( Dc
, rc
.right
, rc
.top
);
705 SelectObject( Dc
, oldPen
);
708 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
713 /* helper lines for debugging */
714 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
715 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
716 SelectObject(Dc
, GetSysColorPen(COLOR_WINDOWFRAME
));
717 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
718 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
723 INT y
= Rect
.top
+ Rect
.bottom
;
725 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
726 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
728 /* Draw the check mark
731 * Custom checkmark bitmaps are monochrome but not always 1bpp.
733 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
735 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
736 if (NULL
!= bm
) /* we have a custom bitmap */
738 HDC DcMem
= CreateCompatibleDC(Dc
);
739 SelectObject(DcMem
, bm
);
740 BitBlt(Dc
, Rc
.left
, (y
- CheckBitmapHeight
) / 2,
741 CheckBitmapWidth
, CheckBitmapHeight
,
742 DcMem
, 0, 0, SRCCOPY
);
746 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
749 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
750 HDC DcMem
= CreateCompatibleDC(Dc
);
751 SelectObject(DcMem
, bm
);
752 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
753 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
754 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
755 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
756 BitBlt(Dc
, Rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
757 DcMem
, 0, 0, SRCCOPY
);
763 if ((Item
->hbmpItem
)&& !( checked
&& (MenuInfo
->dwStyle
& MNS_CHECKORBMP
)))
765 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuInfo
->Self
, WndOwner
, Action
, MenuBar
);
767 /* Draw the popup-menu arrow */
768 if (0 != (Item
->fType
& MF_POPUP
))
770 HDC DcMem
= CreateCompatibleDC(Dc
);
773 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
774 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
775 (y
- ArrowBitmapHeight
) / 2,
776 ArrowBitmapWidth
, ArrowBitmapHeight
,
777 DcMem
, 0, 0, SRCCOPY
);
778 SelectObject(DcMem
, OrigBitmap
);
782 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
783 Rect
.left
+= CheckBitmapWidth
;
784 Rect
.right
-= ArrowBitmapWidth
;
786 else if (Item
->hbmpItem
) /* Draw the bitmap */
788 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuInfo
->Self
, WndOwner
, Action
, MenuBar
);
791 /* No bitmap - process text if present */
795 HFONT FontOld
= NULL
;
797 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
798 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
800 if( !(MenuInfo
->dwStyle
& MNS_CHECKORBMP
))
801 Rect
.left
+= MenuInfo
->maxBmpSize
.cx
;
803 if (0 != (Item
->fState
& MFS_DEFAULT
))
805 FontOld
= SelectObject(Dc
, hMenuFontBold
);
810 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
811 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
813 if (Item
->hbmpItem
== HBMMENU_CALLBACK
|| MenuInfo
->maxBmpSize
.cx
!= 0 )
815 Rect
.left
+= MenuInfo
->maxBmpSize
.cx
;
816 Rect
.right
-= MenuInfo
->maxBmpSize
.cx
;
819 Text
= (PWCHAR
) Item
->dwTypeData
;
822 for (i
= 0; L
'\0' != Text
[i
]; i
++)
824 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
831 if (0 != (Item
->fState
& MF_GRAYED
))
833 if (0 == (Item
->fState
& MF_HILITE
))
835 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
836 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
837 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
838 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
840 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
843 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
845 /* paint the shortcut text */
846 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
848 if (L
'\t' == Text
[i
])
850 Rect
.left
= Item
->XTab
;
851 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
855 Rect
.right
= Item
->XTab
;
856 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
859 if (0 != (Item
->fState
& MF_GRAYED
))
861 if (0 == (Item
->fState
& MF_HILITE
))
863 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
864 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
865 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
866 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
868 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
870 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
875 SelectObject(Dc
, FontOld
);
880 /***********************************************************************
883 * Paint a popup menu.
886 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
888 HBRUSH PrevBrush
= NULL
;
891 ROSMENUINFO MenuInfo
;
892 ROSMENUITEMINFO ItemInfo
;
895 TRACE("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
897 GetClientRect(Wnd
, &Rect
);
899 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
900 && NULL
!= SelectObject(Dc
, hMenuFont
))
902 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
904 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
907 BOOL flat_menu
= FALSE
;
909 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
911 FrameRect(Dc
, &Rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
913 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
915 /* draw menu items */
917 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
919 MenuInitRosMenuItemInfo(&ItemInfo
);
921 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
923 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
925 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
926 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
930 MenuCleanupRosMenuItemInfo(&ItemInfo
);
935 SelectObject(Dc
, PrevBrush
);
940 static LRESULT WINAPI
941 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
943 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
949 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
950 SetWindowLongPtrW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
954 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
955 return MA_NOACTIVATE
;
960 BeginPaint(Wnd
, &ps
);
961 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongPtrW(Wnd
, 0));
970 /* zero out global pointer in case resident popup window was destroyed. */
980 if (0 == GetWindowLongPtrW(Wnd
, 0))
982 OutputDebugStringA("no menu to display\n");
987 SetWindowLongPtrW(Wnd
, 0, 0);
991 case MM_SETMENUHANDLE
:
992 SetWindowLongPtrW(Wnd
, 0, wParam
);
995 case MM_GETMENUHANDLE
:
996 return GetWindowLongPtrW(Wnd
, 0);
999 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
1005 /**********************************************************************
1006 * MENUEX_ParseResource
1008 * Parse an extended menu resource and add items to the menu.
1009 * Return a pointer to the end of the resource.
1011 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
1013 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1021 mii
.cbSize
= sizeof(mii
);
1022 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
1023 mii
.fType
= GET_DWORD(res
);
1024 res
+= sizeof(DWORD
);
1025 mii
.fState
= GET_DWORD(res
);
1026 res
+= sizeof(DWORD
);
1027 mii
.wID
= GET_DWORD(res
);
1028 res
+= sizeof(DWORD
);
1029 resinfo
= GET_WORD(res
);
1030 res
+= sizeof(WORD
);
1031 /* Align the text on a word boundary. */
1032 res
+= (~((int)res
- 1)) & 1;
1033 mii
.dwTypeData
= (LPWSTR
) res
;
1034 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
1035 /* Align the following fields on a dword boundary. */
1036 res
+= (~((int)res
- 1)) & 3;
1038 if (resinfo
& 1) /* Pop-up? */
1040 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1041 res
+= sizeof(DWORD
);
1042 mii
.hSubMenu
= CreatePopupMenu();
1045 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
1047 DestroyMenu(mii
.hSubMenu
);
1050 mii
.fMask
|= MIIM_SUBMENU
;
1051 mii
.fType
|= MF_POPUP
;
1052 mii
.wID
= (UINT
) mii
.hSubMenu
;
1054 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1056 mii
.fType
|= MF_SEPARATOR
;
1058 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1060 while (!(resinfo
& MF_END
));
1065 /**********************************************************************
1066 * MENU_ParseResource
1068 * Parse a standard menu resource and add items to the menu.
1069 * Return a pointer to the end of the resource.
1071 * NOTE: flags is equivalent to the mtOption field
1073 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1082 flags
= GET_WORD(res
);
1084 /* remove MF_END flag before passing it to AppendMenu()! */
1085 end
= (flags
& MF_END
);
1086 if(end
) flags
^= MF_END
;
1088 res
+= sizeof(WORD
);
1089 if(!(flags
& MF_POPUP
))
1092 res
+= sizeof(WORD
);
1096 res
+= strlen(str
) + 1;
1098 res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1099 if (flags
& MF_POPUP
)
1101 hSubMenu
= CreatePopupMenu();
1102 if(!hSubMenu
) return NULL
;
1103 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1106 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1108 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1110 else /* Not a popup */
1113 flags
= MF_SEPARATOR
;
1115 if (flags
& MF_SEPARATOR
)
1117 if (!(flags
& (MF_GRAYED
| MF_DISABLED
)))
1118 flags
|= MF_GRAYED
| MF_DISABLED
;
1122 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1124 AppendMenuW(hMenu
, flags
, id
,
1125 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1134 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1137 Result
= (LRESULT
)LoadMenuW(User32Instance
, L
"SYSMENU");
1138 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1145 NONCLIENTMETRICSW ncm
;
1147 /* get the menu font */
1148 if(!hMenuFont
|| !hMenuFontBold
)
1150 ncm
.cbSize
= sizeof(ncm
);
1151 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1153 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1157 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1158 if(hMenuFont
== NULL
)
1160 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1164 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1165 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1166 if(hMenuFontBold
== NULL
)
1168 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1169 DeleteObject(hMenuFont
);
1184 DeleteObject(hMenuFont
);
1190 DeleteObject(hMenuFontBold
);
1191 hMenuFontBold
= NULL
;
1197 /***********************************************************************
1200 * Calculate the size of the menu item and store it in ItemInfo->rect.
1202 static void FASTCALL
1203 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, PROSMENUINFO MenuInfo
, HWND WndOwner
,
1204 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1208 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1210 TRACE("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1212 MenuCharSize
.cx
= GdiGetCharDimensions( Dc
, NULL
, &MenuCharSize
.cy
);
1214 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1216 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1219 ** Experimentation under Windows reveals that an owner-drawn
1220 ** menu is expected to return the size of the content part of
1221 ** the menu item, not including the checkmark nor the submenu
1222 ** arrow. Windows adds those values itself and returns the
1223 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1225 MEASUREITEMSTRUCT mis
;
1226 mis
.CtlType
= ODT_MENU
;
1228 mis
.itemID
= ItemInfo
->wID
;
1229 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1230 mis
.itemHeight
= HIWORD( GetDialogBaseUnits());
1232 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1233 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1234 * width of a menufont character to the width of an owner-drawn menu.
1236 ItemInfo
->Rect
.right
+= mis
.itemWidth
+ 2 * MenuCharSize
.cx
;
1240 /* under at least win95 you seem to be given a standard
1241 height for the menu and the height value is ignored */
1242 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1246 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1249 TRACE("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1253 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1255 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1257 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
+ MenuCharSize
.cx
;
1263 if (ItemInfo
->hbmpItem
)
1267 if (!MenuBar
) /* hbmpItem */
1269 MenuGetBitmapItemSize(ItemInfo
, &Size
, WndOwner
);
1270 /* Keep the size of the bitmap in callback mode to be able
1271 * to draw it correctly */
1272 ItemInfo
->Rect
.right
= ItemInfo
->Rect
.left
+ Size
.cx
;
1273 if (MenuInfo
->maxBmpSize
.cx
< abs(Size
.cx
) + MENU_ITEM_HBMP_SPACE
||
1274 MenuInfo
->maxBmpSize
.cy
< abs(Size
.cy
))
1276 MenuInfo
->maxBmpSize
.cx
= abs(Size
.cx
) + MENU_ITEM_HBMP_SPACE
;
1277 MenuInfo
->maxBmpSize
.cy
= abs(Size
.cy
);
1279 MenuSetRosMenuInfo(MenuInfo
);
1280 ItemInfo
->Rect
.right
+= Size
.cx
+ 2;
1281 itemheight
= Size
.cy
+ 2;
1283 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
1284 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1285 ItemInfo
->Rect
.right
+= 4 + MenuCharSize
.cx
;
1286 ItemInfo
->XTab
= ItemInfo
->Rect
.right
;
1287 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1289 else /* hbmpItem & MenuBar */
1291 MenuGetBitmapItemSize(ItemInfo
, &Size
, WndOwner
);
1292 ItemInfo
->Rect
.right
+= Size
.cx
;
1293 if( ItemInfo
->Text
) ItemInfo
->Rect
.right
+= 2;
1294 itemheight
= Size
.cy
;
1296 /* Special case: Minimize button doesn't have a space behind it. */
1297 if (ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1298 ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1299 ItemInfo
->Rect
.right
-= 1;
1304 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
1305 ItemInfo
->Rect
.right
+= CheckBitmapWidth
;
1306 ItemInfo
->Rect
.right
+= 4 + MenuCharSize
.cx
;
1307 ItemInfo
->XTab
= ItemInfo
->Rect
.right
;
1308 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1311 /* it must be a text item - unless it's the system menu */
1312 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && ItemInfo
->Text
)
1314 HFONT hfontOld
= NULL
;
1315 RECT rc
= ItemInfo
->Rect
;
1316 LONG txtheight
, txtwidth
;
1318 if ( ItemInfo
->fState
& MFS_DEFAULT
)
1320 hfontOld
= SelectObject( Dc
, hMenuFontBold
);
1324 txtheight
= DrawTextW( Dc
, ItemInfo
->dwTypeData
, -1, &rc
,
1325 DT_SINGLELINE
|DT_CALCRECT
);
1326 ItemInfo
->Rect
.right
+= rc
.right
- rc
.left
;
1327 itemheight
= max( max( itemheight
, txtheight
),
1328 GetSystemMetrics( SM_CYMENU
) - 1);
1329 ItemInfo
->Rect
.right
+= 2 * MenuCharSize
.cx
;
1333 if ((p
= strchrW( ItemInfo
->dwTypeData
, '\t' )) != NULL
)
1337 int n
= (int)( p
- ItemInfo
->dwTypeData
);
1338 /* Item contains a tab (only meaningful in popup menus) */
1339 /* get text size before the tab */
1340 txtheight
= DrawTextW( Dc
, ItemInfo
->dwTypeData
, n
, &rc
,
1341 DT_SINGLELINE
|DT_CALCRECT
);
1342 txtwidth
= rc
.right
- rc
.left
;
1343 p
+= 1; /* advance past the Tab */
1344 /* get text size after the tab */
1345 tmpheight
= DrawTextW( Dc
, p
, -1, &tmprc
, DT_SINGLELINE
|DT_CALCRECT
);
1346 ItemInfo
->XTab
+= txtwidth
;
1347 txtheight
= max( txtheight
, tmpheight
);
1348 txtwidth
+= MenuCharSize
.cx
+ /* space for the tab */
1349 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1353 txtheight
= DrawTextW( Dc
, ItemInfo
->dwTypeData
, -1, &rc
,
1354 DT_SINGLELINE
|DT_CALCRECT
);
1355 txtwidth
= rc
.right
- rc
.left
;
1356 ItemInfo
->XTab
+= txtwidth
;
1358 ItemInfo
->Rect
.right
+= 2 + txtwidth
;
1359 itemheight
= max( itemheight
, max( txtheight
+ 2, MenuCharSize
.cy
+ 4));
1361 if (hfontOld
) SelectObject (Dc
, hfontOld
);
1365 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1367 ItemInfo
->Rect
.bottom
+= itemheight
;
1368 TRACE("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1371 /***********************************************************************
1372 * MenuPopupMenuCalcSize
1374 * Calculate the size of a popup menu.
1376 static void FASTCALL
1377 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1379 ROSMENUITEMINFO ItemInfo
;
1382 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1384 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1385 if (0 == MenuInfo
->MenuItemCount
)
1387 MenuSetRosMenuInfo(MenuInfo
);
1392 SelectObject(Dc
, hMenuFont
);
1397 MenuInfo
->maxBmpSize
.cx
= 0;
1398 MenuInfo
->maxBmpSize
.cy
= 0;
1400 MenuInitRosMenuItemInfo(&ItemInfo
);
1401 while (Start
< MenuInfo
->MenuItemCount
)
1406 MaxTab
= MaxTabWidth
= 0;
1408 /* Parse items until column break or end of menu */
1409 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1411 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1413 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1414 MenuSetRosMenuInfo(MenuInfo
);
1418 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1422 MenuCalcItemSize(Dc
, &ItemInfo
, MenuInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1423 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1425 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1426 MenuSetRosMenuInfo(MenuInfo
);
1429 // Not sure here,, The patch from wine removes this.
1430 // if (0 != (ItemInfo.fType & MF_MENUBARBREAK))
1434 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1435 OrgY
= ItemInfo
.Rect
.bottom
;
1436 if ((ItemInfo
.Text
) && 0 != ItemInfo
.XTab
)
1438 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1439 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1443 /* Finish the column (set all items to the largest width found) */
1444 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1447 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1449 ItemInfo
.Rect
.right
= MaxX
;
1450 if ((ItemInfo
.Text
) && 0 != ItemInfo
.XTab
)
1452 ItemInfo
.XTab
= MaxTab
;
1454 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1458 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1461 MenuInfo
->Width
= MaxX
;
1463 /* space for 3d border */
1464 MenuInfo
->Height
+= 2;
1465 MenuInfo
->Width
+= 2;
1467 ReleaseDC(NULL
, Dc
);
1468 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1469 MenuSetRosMenuInfo(MenuInfo
);
1472 /***********************************************************************
1473 * MenuMenuBarCalcSize
1475 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1476 * height is off by 1 pixel which causes lengthy window relocations when
1477 * active document window is maximized/restored.
1479 * Calculate the size of the menu bar.
1481 static void FASTCALL
1482 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1484 ROSMENUITEMINFO ItemInfo
;
1485 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1487 if (NULL
== Rect
|| NULL
== MenuInfo
)
1491 if (0 == MenuInfo
->MenuItemCount
)
1496 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1497 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1498 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1499 MenuInfo
->Height
= 0;
1500 MaxY
= Rect
->top
+ 1;
1504 MenuInfo
->maxBmpSize
.cx
= 0;
1505 MenuInfo
->maxBmpSize
.cy
= 0;
1507 MenuInitRosMenuItemInfo(&ItemInfo
);
1508 while (Start
< MenuInfo
->MenuItemCount
)
1510 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1512 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1518 /* Parse items until line break or end of menu */
1519 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1521 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1526 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1531 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1532 MenuCalcItemSize(Dc
, &ItemInfo
, MenuInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1533 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1535 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1539 if (ItemInfo
.Rect
.right
> Rect
->right
)
1547 ItemInfo
.Rect
.right
= Rect
->right
;
1550 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1551 OrgX
= ItemInfo
.Rect
.right
;
1552 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1554 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1556 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1562 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1563 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1565 /* Finish the line (set all items to the largest height found) */
1568 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1570 ItemInfo
.Rect
.bottom
= MaxY
;
1571 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1576 Start
= i
; /* This works! */
1580 Rect
->bottom
= MaxY
;
1581 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1582 MenuSetRosMenuInfo(MenuInfo
);
1586 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1587 /* the last item (if several lines, only move the last line) */
1588 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1590 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1593 OrgY
= ItemInfo
.Rect
.top
;
1595 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1601 if (ItemInfo
.Rect
.top
!= OrgY
)
1603 break; /* Other line */
1605 if (OrgX
<= ItemInfo
.Rect
.right
)
1607 break; /* Too far right already */
1609 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1610 ItemInfo
.Rect
.right
= OrgX
;
1611 OrgX
= ItemInfo
.Rect
.left
;
1612 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1613 if (HelpPos
+ 1 <= i
&&
1614 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1616 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1622 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1625 /***********************************************************************
1626 * DrawMenuBarTemp (USER32.@)
1630 * called by W98SE desk.cpl Control Panel Applet
1632 * Not 100% sure about the param names, but close.
1637 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1639 ROSMENUINFO MenuInfo
;
1640 ROSMENUITEMINFO ItemInfo
;
1642 HFONT FontOld
= NULL
;
1643 BOOL flat_menu
= FALSE
;
1645 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1649 Menu
= GetMenu(Wnd
);
1657 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1659 return GetSystemMetrics(SM_CYMENU
);
1662 TRACE("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1664 FontOld
= SelectObject(DC
, Font
);
1666 if (0 == MenuInfo
.Height
)
1668 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1671 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1673 FillRect(DC
, Rect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
));
1675 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1676 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1677 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1679 if (0 == MenuInfo
.MenuItemCount
)
1681 SelectObject(DC
, FontOld
);
1682 return GetSystemMetrics(SM_CYMENU
);
1685 MenuInitRosMenuItemInfo(&ItemInfo
);
1686 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1688 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1690 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1691 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1694 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1696 SelectObject(DC
, FontOld
);
1698 return MenuInfo
.Height
;
1702 /***********************************************************************
1705 * Paint a menu bar. Returns the height of the menu bar.
1706 * called from [windows/nonclient.c]
1708 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1710 ROSMENUINFO MenuInfo
;
1711 HFONT FontOld
= NULL
;
1712 HMENU Menu
= GetMenu(Wnd
);
1714 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1716 return GetSystemMetrics(SM_CYMENU
);
1721 FontOld
= SelectObject(DC
, hMenuFont
);
1723 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1725 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1727 if (NULL
!= FontOld
)
1729 SelectObject(DC
, FontOld
);
1731 return MenuInfo
.Height
;
1735 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1739 /***********************************************************************
1742 static BOOL FASTCALL
1743 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1745 TRACE("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1749 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1750 if (0 == (Flags
& TPM_NONOTIFY
))
1752 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1755 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1757 if (0 == (Flags
& TPM_NONOTIFY
))
1759 ROSMENUINFO MenuInfo
;
1761 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1763 MenuGetRosMenuInfo(&MenuInfo
, Menu
);
1765 if (0 == MenuInfo
.Height
)
1767 /* app changed/recreated menu bar entries in WM_INITMENU
1768 Recalculate menu sizes else clicks will not work */
1769 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1770 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1773 /* This makes the menus of applications built with Delphi work.
1774 * It also enables menus to be displayed in more than one window,
1775 * but there are some bugs left that need to be fixed in this case.
1777 if(MenuInfo
.Self
== Menu
)
1780 MenuSetRosMenuInfo(&MenuInfo
);
1788 /***********************************************************************
1791 * Display a popup menu.
1793 static BOOL FASTCALL
1794 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1795 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1797 ROSMENUINFO MenuInfo
;
1798 ROSMENUITEMINFO ItemInfo
;
1801 TRACE("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1802 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1804 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1809 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1811 MenuInitRosMenuItemInfo(&ItemInfo
);
1812 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1814 ItemInfo
.fMask
|= MIIM_STATE
;
1815 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1816 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1818 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1819 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1822 /* store the owner for DrawItem */
1823 MenuInfo
.WndOwner
= WndOwner
;
1824 MenuSetRosMenuInfo(&MenuInfo
);
1826 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1828 /* adjust popup menu pos so that it fits within the desktop */
1830 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1831 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1833 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1835 if (0 != XAnchor
&& X
>= Width
- XAnchor
)
1837 X
-= Width
- XAnchor
;
1839 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1841 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1849 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1851 if (0 != YAnchor
&& Y
>= Height
+ YAnchor
)
1853 Y
-= Height
+ YAnchor
;
1855 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1857 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1866 /* NOTE: In Windows, top menu popup is not owned. */
1867 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1868 WS_POPUP
, X
, Y
, Width
, Height
,
1869 WndOwner
, 0, (HINSTANCE
) GetWindowLongPtrW(WndOwner
, GWLP_HINSTANCE
),
1870 (LPVOID
) MenuInfo
.Self
);
1871 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1875 if (NULL
== TopPopup
)
1877 TopPopup
= MenuInfo
.Wnd
;
1880 /* Display the window */
1881 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1882 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1883 UpdateWindow(MenuInfo
.Wnd
);
1888 /***********************************************************************
1891 * Find a Sub menu. Return the position of the submenu, and modifies
1892 * *hmenu in case it is found in another sub-menu.
1893 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1895 static UINT FASTCALL
1896 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1898 ROSMENUINFO MenuInfo
;
1899 ROSMENUITEMINFO ItemInfo
;
1904 if ((HMENU
) 0xffff == *Menu
1905 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1907 return NO_SELECTED_ITEM
;
1910 MenuInitRosMenuItemInfo(&ItemInfo
);
1911 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1913 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1915 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1916 return NO_SELECTED_ITEM
;
1918 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1922 if (ItemInfo
.hSubMenu
== SubTarget
)
1924 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1927 SubMenu
= ItemInfo
.hSubMenu
;
1928 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1929 if (NO_SELECTED_ITEM
!= Pos
)
1935 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1937 return NO_SELECTED_ITEM
;
1940 /***********************************************************************
1943 static void FASTCALL
1944 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1945 BOOL SendMenuSelect
, HMENU TopMenu
)
1948 ROSMENUITEMINFO ItemInfo
;
1949 ROSMENUINFO TopMenuInfo
;
1952 TRACE("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1954 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1959 if (MenuInfo
->FocusedItem
== Index
)
1964 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1966 Dc
= GetDC(MenuInfo
->Wnd
);
1970 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1973 if (NULL
== TopPopup
)
1975 TopPopup
= MenuInfo
->Wnd
;
1978 SelectObject(Dc
, hMenuFont
);
1979 MenuInitRosMenuItemInfo(&ItemInfo
);
1980 /* Clear previous highlighted item */
1981 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1983 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1985 ItemInfo
.fMask
|= MIIM_STATE
;
1986 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1987 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1989 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1990 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1994 /* Highlight new item (if any) */
1995 MenuInfo
->FocusedItem
= Index
;
1996 MenuSetRosMenuInfo(MenuInfo
);
1997 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1999 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2001 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2003 ItemInfo
.fMask
|= MIIM_STATE
;
2004 ItemInfo
.fState
|= MF_HILITE
;
2005 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2006 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
2007 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
2012 SendMessageW(WndOwner
, WM_MENUSELECT
,
2013 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
2014 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
2015 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
2019 else if (SendMenuSelect
)
2021 if (NULL
!= TopMenu
)
2023 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
2024 if (NO_SELECTED_ITEM
!= Pos
)
2026 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
2027 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
2029 SendMessageW(WndOwner
, WM_MENUSELECT
,
2030 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
2032 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
2038 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2039 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2042 /***********************************************************************
2045 * Moves currently selected item according to the Offset parameter.
2046 * If there is no selection then it should select the last item if
2047 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
2049 static void FASTCALL
2050 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
2053 ROSMENUITEMINFO ItemInfo
;
2056 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
2058 /* Prevent looping */
2059 if (0 == MenuInfo
->MenuItemCount
|| 0 == Offset
)
2061 else if (Offset
< -1)
2063 else if (Offset
> 1)
2066 MenuInitRosMenuItemInfo(&ItemInfo
);
2068 OrigPos
= MenuInfo
->FocusedItem
;
2069 if (OrigPos
== NO_SELECTED_ITEM
) /* NO_SELECTED_ITEM is not -1 ! */
2076 i
= MenuInfo
->FocusedItem
;
2083 /* Clip and wrap around */
2086 i
= MenuInfo
->MenuItemCount
- 1;
2088 else if (i
>= MenuInfo
->MenuItemCount
)
2092 /* If this is a good candidate; */
2093 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
2094 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2096 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
2097 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2100 } while (i
!= OrigPos
);
2103 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2106 /***********************************************************************
2107 * MenuInitSysMenuPopup
2109 * Grey the appropriate items in System menu.
2112 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
2120 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
2121 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2122 Gray
= 0 != (Style
& WS_MAXIMIZE
);
2123 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2124 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
2125 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2126 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
2127 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2128 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
2129 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2130 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
2132 /* The menu item must keep its state if it's disabled */
2135 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
2138 /* Set default menu item */
2139 if(Style
& WS_MINIMIZE
)
2141 DefItem
= SC_RESTORE
;
2145 if(HitTest
== HTCAPTION
)
2147 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
2155 mii
.cbSize
= sizeof(MENUITEMINFOW
);
2156 mii
.fMask
|= MIIM_STATE
;
2157 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
2158 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
2163 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
2166 /***********************************************************************
2169 * Display the sub-menu of the selected item of this menu.
2170 * Return the handle of the submenu, or menu if no submenu to display.
2172 static HMENU FASTCALL
2173 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2175 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2177 ROSMENUITEMINFO ItemInfo
;
2178 ROSMENUINFO SubMenuInfo
;
2182 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2184 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2186 return MenuInfo
->Self
;
2189 MenuInitRosMenuItemInfo(&ItemInfo
);
2190 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2192 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2193 return MenuInfo
->Self
;
2195 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2197 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2198 return MenuInfo
->Self
;
2201 /* message must be sent before using item,
2202 because nearly everything may be changed by the application ! */
2204 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2205 if (0 == (Flags
& TPM_NONOTIFY
))
2207 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2208 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2211 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2213 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2214 return MenuInfo
->Self
;
2216 Rect
= ItemInfo
.Rect
;
2218 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2219 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2221 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2223 Dc
= GetDC(MenuInfo
->Wnd
);
2227 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2230 SelectObject(Dc
, hMenuFont
);
2231 ItemInfo
.fMask
|= MIIM_STATE
;
2232 ItemInfo
.fState
|= MF_HILITE
;
2233 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2234 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2235 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2236 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2239 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2240 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2242 ItemInfo
.Rect
= Rect
;
2245 ItemInfo
.fMask
|= MIIM_STATE
;
2246 ItemInfo
.fState
|= MF_MOUSESELECT
;
2247 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2249 if (IS_SYSTEM_MENU(MenuInfo
))
2251 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2252 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2254 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2255 Rect
.top
= Rect
.bottom
;
2256 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2257 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2261 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2262 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2264 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2265 Rect
.top
+= ItemInfo
.Rect
.top
- 3;
2266 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2267 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
- 3 - 2
2268 - GetSystemMetrics(SM_CYBORDER
);
2272 Rect
.left
+= ItemInfo
.Rect
.left
;
2273 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2274 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2275 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2279 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2280 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2281 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2283 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2286 Ret
= ItemInfo
.hSubMenu
;
2287 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2292 /***********************************************************************
2295 * Hide the sub-popup menus of this menu.
2297 static void FASTCALL
2298 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2300 ROSMENUINFO SubMenuInfo
;
2301 ROSMENUITEMINFO ItemInfo
;
2303 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2305 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2307 MenuInitRosMenuItemInfo(&ItemInfo
);
2308 ItemInfo
.fMask
|= MIIM_FTYPE
| MIIM_STATE
;
2309 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2310 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2311 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2313 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2316 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2317 ItemInfo
.fMask
|= MIIM_STATE
;
2318 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2319 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2321 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2322 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2323 DestroyWindow(SubMenuInfo
.Wnd
);
2324 SubMenuInfo
.Wnd
= NULL
;
2325 MenuSetRosMenuInfo(&SubMenuInfo
);
2330 /***********************************************************************
2331 * MenuSwitchTracking
2333 * Helper function for menu navigation routines.
2335 static void FASTCALL
2336 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2338 ROSMENUINFO TopMenuInfo
;
2340 TRACE("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2342 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2343 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2344 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2346 /* both are top level menus (system and menu-bar) */
2347 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2348 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2349 Mt
->TopMenu
= PtMenuInfo
->Self
;
2353 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2356 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2359 /***********************************************************************
2360 * MenuExecFocusedItem
2362 * Execute a menu item (for instance when user pressed Enter).
2363 * Return the wID of the executed item. Otherwise, -1 indicating
2364 * that no menu item was executed, -2 if a popup is shown;
2365 * Have to receive the flags for the TrackPopupMenu options to avoid
2366 * sending unwanted message.
2370 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2372 ROSMENUITEMINFO ItemInfo
;
2375 TRACE("%p menu=%p\n", Mt
, MenuInfo
);
2377 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2382 MenuInitRosMenuItemInfo(&ItemInfo
);
2383 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2385 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2389 TRACE("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2391 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2393 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2394 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2396 /* If TPM_RETURNCMD is set you return the id, but
2397 do not send a message to the owner */
2398 if (0 == (Flags
& TPM_RETURNCMD
))
2400 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2402 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2403 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2407 if (MenuInfo
->dwStyle
& MNS_NOTIFYBYPOS
)
2408 PostMessageW(Mt
->OwnerWnd
, WM_MENUCOMMAND
,
2409 MenuInfo
->FocusedItem
,
2410 (LPARAM
)MenuInfo
->Self
);
2412 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2416 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2422 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2429 /***********************************************************************
2432 * Return TRUE if we can go on with menu tracking.
2434 static BOOL FASTCALL
2435 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2438 ROSMENUINFO MenuInfo
;
2439 ROSMENUITEMINFO Item
;
2441 TRACE("%x PtMenu=%p\n", Mt
, PtMenu
);
2445 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2449 if (IS_SYSTEM_MENU(&MenuInfo
))
2455 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2457 MenuInitRosMenuItemInfo(&Item
);
2458 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2460 MenuCleanupRosMenuItemInfo(&Item
);
2464 if (!(Item
.fType
& MF_SEPARATOR
) &&
2465 !(Item
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2467 if (MenuInfo
.FocusedItem
!= Index
)
2469 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2472 /* If the popup menu is not already "popped" */
2473 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2475 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2479 MenuCleanupRosMenuItemInfo(&Item
);
2484 /* else the click was on the menu bar, finish the tracking */
2489 /***********************************************************************
2492 * Return the value of MenuExecFocusedItem if
2493 * the selected item was not a popup. Else open the popup.
2494 * A -1 return value indicates that we go on with menu tracking.
2498 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2501 ROSMENUINFO MenuInfo
;
2502 ROSMENUITEMINFO ItemInfo
;
2504 TRACE("%p hmenu=%x\n", Mt
, PtMenu
);
2509 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2514 if (! IS_SYSTEM_MENU(&MenuInfo
))
2516 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2518 MenuInitRosMenuItemInfo(&ItemInfo
);
2519 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2520 MenuInfo
.FocusedItem
== Id
)
2522 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2524 INT ExecutedMenuId
= MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2525 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2526 return (ExecutedMenuId
< 0) ? -1 : ExecutedMenuId
;
2528 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2530 /* If we are dealing with the top-level menu */
2531 /* and this is a click on an already "popped" item: */
2532 /* Stop the menu tracking and close the opened submenus */
2533 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2535 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2539 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2540 MenuInfo
.TimeToHide
= TRUE
;
2541 MenuSetRosMenuInfo(&MenuInfo
);
2547 /***********************************************************************
2550 * Walks menu chain trying to find a menu pt maps to.
2552 static HMENU FASTCALL
2553 MenuPtMenu(HMENU Menu
, POINT Pt
)
2555 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2556 ROSMENUINFO MenuInfo
;
2557 ROSMENUITEMINFO ItemInfo
;
2561 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2566 /* try subpopup first (if any) */
2567 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2569 MenuInitRosMenuItemInfo(&ItemInfo
);
2570 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2571 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2572 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2574 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2577 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2581 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2584 /* check the current window (avoiding WM_HITTEST) */
2585 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2586 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2588 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2593 else if (HTSYSMENU
== Ht
)
2595 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2597 else if (HTMENU
== Ht
)
2599 Ret
= GetMenu(MenuInfo
.Wnd
);
2605 /***********************************************************************
2608 * Return TRUE if we can go on with menu tracking.
2610 static BOOL FASTCALL
2611 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2614 ROSMENUINFO MenuInfo
;
2615 ROSMENUITEMINFO ItemInfo
;
2619 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2623 if (IS_SYSTEM_MENU(&MenuInfo
))
2629 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2634 Index
= NO_SELECTED_ITEM
;
2637 if (NO_SELECTED_ITEM
== Index
)
2639 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2640 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2642 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2646 else if (MenuInfo
.FocusedItem
!= Index
)
2648 MenuInitRosMenuItemInfo(&ItemInfo
);
2649 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, Index
, &ItemInfo
) &&
2650 !(ItemInfo
.fType
& MF_SEPARATOR
))
2652 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2653 if (!(ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)))
2654 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2656 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2662 /******************************************************************************
2664 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2666 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2669 PROSMENUITEMINFO MenuItems
;
2671 i
= MenuInfo
->FocusedItem
;
2672 if (NO_SELECTED_ITEM
== i
)
2677 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2679 return NO_SELECTED_ITEM
;
2682 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2684 if (0 != (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
2690 return NO_SELECTED_ITEM
;
2693 /******************************************************************************
2695 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2697 static UINT FASTCALL
2698 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2701 PROSMENUITEMINFO MenuItems
;
2703 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2705 return NO_SELECTED_ITEM
;
2708 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2710 return NO_SELECTED_ITEM
;
2713 /* Find the start of the column */
2715 for (i
= MenuInfo
->FocusedItem
;
2716 0 != i
&& 0 == (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
2724 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2725 return NO_SELECTED_ITEM
;
2728 for (--i
; 0 != i
; --i
)
2730 if (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
2736 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2737 TRACE("ret %d.\n", i
);
2742 /***********************************************************************
2745 * Return the handle of the selected sub-popup menu (if any).
2747 static HMENU FASTCALL
2748 MenuGetSubPopup(HMENU Menu
)
2750 ROSMENUINFO MenuInfo
;
2751 ROSMENUITEMINFO ItemInfo
;
2753 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2754 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2759 MenuInitRosMenuItemInfo(&ItemInfo
);
2760 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2762 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2765 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2767 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2768 return ItemInfo
.hSubMenu
;
2771 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2775 /***********************************************************************
2778 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2780 static LRESULT FASTCALL
2781 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2783 ROSMENUINFO TopMenuInfo
;
2784 ROSMENUINFO MenuInfo
;
2786 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2788 return (LRESULT
) FALSE
;
2791 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2792 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2794 MDINEXTMENU NextMenu
;
2799 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2800 NextMenu
.hmenuNext
= NULL
;
2801 NextMenu
.hwndNext
= NULL
;
2802 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2804 TRACE("%p [%p] -> %p [%p]\n",
2805 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2807 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2809 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2810 NewWnd
= Mt
->OwnerWnd
;
2811 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2813 /* switch to the menu bar */
2815 if (0 != (Style
& WS_CHILD
)
2816 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2823 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2827 Id
= MenuInfo
.MenuItemCount
- 1;
2830 else if (0 != (Style
& WS_SYSMENU
))
2832 /* switch to the system menu */
2833 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2840 else /* application returned a new menu to switch to */
2842 NewMenu
= NextMenu
.hmenuNext
;
2843 NewWnd
= NextMenu
.hwndNext
;
2845 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2847 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2849 if (0 != (Style
& WS_SYSMENU
)
2850 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2852 /* get the real system menu */
2853 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2855 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2857 /* FIXME: Not sure what to do here;
2858 * perhaps try to track NewMenu as a popup? */
2860 WARN(" -- got confused.\n");
2870 if (NewMenu
!= Mt
->TopMenu
)
2872 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2874 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2876 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2880 if (NewWnd
!= Mt
->OwnerWnd
)
2882 Mt
->OwnerWnd
= NewWnd
;
2883 SetCapture(Mt
->OwnerWnd
);
2884 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2887 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2888 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2890 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2899 /***********************************************************************
2902 * The idea is not to show the popup if the next input message is
2903 * going to hide it anyway.
2905 static BOOL FASTCALL
2906 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2910 Msg
.hwnd
= Mt
->OwnerWnd
;
2912 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2913 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2918 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2919 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2921 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2922 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2923 if (WM_KEYDOWN
== Msg
.message
2924 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2926 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2933 /* failures go through this */
2934 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2939 /***********************************************************************
2942 * Handle a VK_ESCAPE key event in a menu.
2944 static BOOL FASTCALL
2945 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2947 BOOL EndMenu
= TRUE
;
2948 ROSMENUINFO MenuInfo
;
2949 HMENU MenuTmp
, MenuPrev
;
2951 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2953 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2954 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2956 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2958 /* close topmost popup */
2959 while (MenuTmp
!= Mt
->CurrentMenu
)
2962 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2965 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2967 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2969 Mt
->CurrentMenu
= MenuPrev
;
2977 /***********************************************************************
2980 * Handle a VK_LEFT key event in a menu.
2982 static void FASTCALL
2983 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2985 ROSMENUINFO MenuInfo
;
2986 ROSMENUINFO TopMenuInfo
;
2987 ROSMENUINFO PrevMenuInfo
;
2988 HMENU MenuTmp
, MenuPrev
;
2991 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2993 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2998 /* Try to move 1 column left (if possible) */
2999 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
3001 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
3003 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
3008 /* close topmost popup */
3009 while (MenuTmp
!= Mt
->CurrentMenu
)
3012 MenuTmp
= MenuGetSubPopup(MenuPrev
);
3015 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
3019 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
3020 Mt
->CurrentMenu
= MenuPrev
;
3022 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
3026 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
3028 /* move menu bar selection if no more popups are left */
3030 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
3032 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
3035 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
3037 /* A sublevel menu was displayed - display the next one
3038 * unless there is another displacement coming up */
3040 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
3041 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
3043 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
3050 /***********************************************************************
3053 * Handle a VK_RIGHT key event in a menu.
3055 static void FASTCALL
3056 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
3059 ROSMENUINFO MenuInfo
;
3060 ROSMENUINFO CurrentMenuInfo
;
3063 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3064 Mt
->CurrentMenu
, Mt
->TopMenu
);
3066 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
3070 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
3072 /* If already displaying a popup, try to display sub-popup */
3074 MenuTmp
= Mt
->CurrentMenu
;
3075 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
3077 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
3080 /* if subpopup was displayed then we are done */
3081 if (MenuTmp
!= Mt
->CurrentMenu
)
3087 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
3092 /* Check to see if there's another column */
3093 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
3095 TRACE("Going to %d.\n", NextCol
);
3096 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
3098 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
3103 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
3105 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
3107 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
3108 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
3115 /* try to move to the next item */
3116 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
3118 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3121 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
3123 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
3124 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
3126 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
3133 /***********************************************************************
3136 * Find the menu item selected by a key press.
3137 * Return item id, -1 if none, -2 if we should close the menu.
3139 static UINT FASTCALL
3140 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
3141 WCHAR Key
, BOOL ForceMenuChar
)
3143 ROSMENUINFO SysMenuInfo
;
3144 PROSMENUITEMINFO Items
, ItemInfo
;
3148 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
3150 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
3152 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
3154 MenuInfo
= &SysMenuInfo
;
3162 if (NULL
!= MenuInfo
)
3164 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
3168 if (! ForceMenuChar
)
3170 Key
= toupperW(Key
);
3172 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
3174 if ((ItemInfo
->Text
) && NULL
!= ItemInfo
->dwTypeData
)
3176 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
3179 p
= strchrW(p
+ 2, '&');
3181 while (NULL
!= p
&& L
'&' == p
[1]);
3182 if (NULL
!= p
&& (toupperW(p
[1]) == Key
))
3190 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3191 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3192 if (2 == HIWORD(MenuChar
))
3194 return LOWORD(MenuChar
);
3196 if (1 == HIWORD(MenuChar
))
3205 /***********************************************************************
3208 * Menu tracking code.
3211 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3212 HWND Wnd
, const RECT
*Rect
)
3215 ROSMENUINFO MenuInfo
;
3216 ROSMENUITEMINFO ItemInfo
;
3218 INT ExecutedMenuId
= -1;
3220 BOOL EnterIdleSent
= FALSE
;
3223 Mt
.CurrentMenu
= Menu
;
3229 TRACE("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3230 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3231 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3234 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3239 if (0 != (Flags
& TPM_BUTTONDOWN
))
3241 /* Get the result in order to start the tracking or not */
3242 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3243 fEndMenu
= ! fRemove
;
3246 SetCapture(Mt
.OwnerWnd
);
3247 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3251 /* we have to keep the message in the queue until it's
3252 * clear that menu loop is not over yet. */
3256 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3258 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3262 /* remove the message from the queue */
3263 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3267 if (! EnterIdleSent
)
3269 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3270 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3271 EnterIdleSent
= TRUE
;
3272 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3278 /* check if EndMenu() tried to cancel us, by posting this message */
3279 if (WM_CANCELMODE
== Msg
.message
)
3281 /* we are now out of the loop */
3284 /* remove the message from the queue */
3285 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3287 /* break out of internal loop, ala ESCAPE */
3291 TranslateMessage(&Msg
);
3294 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3296 EnterIdleSent
= FALSE
;
3300 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3303 * Use the mouse coordinates in lParam instead of those in the MSG
3304 * struct to properly handle synthetic messages. They are already
3305 * in screen coordinates.
3307 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3308 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3310 /* Find a menu for this mouse event */
3311 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3315 /* no WM_NC... messages in captured state */
3317 case WM_RBUTTONDBLCLK
:
3318 case WM_RBUTTONDOWN
:
3319 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3324 case WM_LBUTTONDBLCLK
:
3325 case WM_LBUTTONDOWN
:
3326 /* If the message belongs to the menu, removes it from the queue */
3327 /* Else, end menu tracking */
3328 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3329 fEndMenu
= ! fRemove
;
3333 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3339 /* Check if a menu was selected by the mouse */
3342 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3344 /* End the loop if ExecutedMenuId is an item ID */
3345 /* or if the job was done (ExecutedMenuId = 0). */
3346 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3350 /* No menu was selected by the mouse */
3351 /* if the function was called by TrackPopupMenu, continue
3352 with the menu tracking. If not, stop it */
3353 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3360 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3364 } /* switch(Msg.message) - mouse */
3366 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3368 fRemove
= TRUE
; /* Keyboard messages are always removed */
3380 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3382 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3388 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3390 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3391 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3395 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3396 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3398 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3400 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3402 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3406 else /* otherwise try to move selection */
3408 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3414 MenuKeyLeft(&Mt
, Flags
);
3418 MenuKeyRight(&Mt
, Flags
);
3422 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3428 hi
.cbSize
= sizeof(HELPINFO
);
3429 hi
.iContextType
= HELPINFO_MENUITEM
;
3430 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3432 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3438 MenuInitRosMenuItemInfo(&ItemInfo
);
3439 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3440 MenuInfo
.FocusedItem
,
3443 hi
.iCtrlId
= ItemInfo
.wID
;
3449 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3452 hi
.hItemHandle
= Menu
;
3453 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3454 hi
.MousePos
= Msg
.pt
;
3455 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3462 break; /* WM_KEYDOWN */
3469 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3473 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3475 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3476 fEndMenu
= (ExecutedMenuId
!= -2);
3480 /* Hack to avoid control chars. */
3481 /* We will find a better way real soon... */
3482 if (Msg
.wParam
< 32)
3487 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3488 LOWORD(Msg
.wParam
), FALSE
);
3489 if ((UINT
) -2 == Pos
)
3493 else if ((UINT
) -1 == Pos
)
3499 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3500 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3501 fEndMenu
= (-2 != ExecutedMenuId
);
3505 } /* switch(msg.message) - kbd */
3509 PeekMessageW( &Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3510 DispatchMessageW(&Msg
);
3519 /* finally remove message from the queue */
3521 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3523 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3527 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3531 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3532 SetCapture(NULL
); /* release the capture */
3534 /* If dropdown is still painted and the close box is clicked on
3535 then the menu will be destroyed as part of the DispatchMessage above.
3536 This will then invalidate the menu handle in Mt.hTopMenu. We should
3537 check for this first. */
3538 if (IsMenu(Mt
.TopMenu
))
3540 if (IsWindow(Mt
.OwnerWnd
))
3542 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3544 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3546 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3548 DestroyWindow(MenuInfo
.Wnd
);
3549 MenuInfo
.Wnd
= NULL
;
3551 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3554 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3557 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3559 /* Reset the variable for hiding menu */
3560 MenuInfo
.TimeToHide
= FALSE
;
3561 MenuSetRosMenuInfo(&MenuInfo
);
3565 /* The return value is only used by TrackPopupMenu */
3566 if (!(Flags
& TPM_RETURNCMD
)) return TRUE
;
3567 if (ExecutedMenuId
< 0) ExecutedMenuId
= 0;
3568 return ExecutedMenuId
;
3571 /***********************************************************************
3574 static BOOL FASTCALL
3575 MenuExitTracking(HWND Wnd
)
3577 TRACE("hwnd=%p\n", Wnd
);
3579 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3586 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3588 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3589 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3591 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3595 /* map point to parent client coordinates */
3596 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3597 if (Parent
!= GetDesktopWindow())
3599 ScreenToClient(Parent
, &Pt
);
3602 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3603 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3604 MenuExitTracking(Wnd
);
3610 MenuTrackKbdMenuBar(HWND hWnd
, UINT wParam
, WCHAR wChar
)
3612 UINT uItem
= NO_SELECTED_ITEM
;
3614 ROSMENUINFO MenuInfo
;
3615 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3617 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hWnd
, wParam
, wChar
);
3619 /* find window that has a menu */
3621 while (!((GetWindowLongW( hWnd
, GWL_STYLE
) &
3622 (WS_CHILD
| WS_POPUP
)) != WS_CHILD
))
3623 if (!(hWnd
= GetAncestor( hWnd
, GA_PARENT
))) return;
3625 /* check if we have to track a system menu */
3627 hTrackMenu
= GetMenu( hWnd
);
3628 if (!hTrackMenu
|| IsIconic(hWnd
) || wChar
== ' ' )
3630 if (!(GetWindowLongW( hWnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3631 hTrackMenu
= NtUserGetSystemMenu(hWnd
, FALSE
);
3633 wParam
|= HTSYSMENU
; /* prevent item lookup */
3636 if (!IsMenu( hTrackMenu
)) return;
3638 MenuInitTracking( hWnd
, hTrackMenu
, FALSE
, wFlags
);
3640 if (! MenuGetRosMenuInfo(&MenuInfo
, hTrackMenu
))
3645 if( wChar
&& wChar
!= ' ' )
3647 uItem
= MenuFindItemByKey( hWnd
, &MenuInfo
, wChar
, (wParam
& HTSYSMENU
) );
3648 if ( uItem
>= (UINT
)(-2) )
3650 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3651 /* schedule end of menu tracking */
3652 wFlags
|= TF_ENDMENU
;
3657 MenuSelectItem( hWnd
, &MenuInfo
, uItem
, TRUE
, 0 );
3659 if (wParam
& HTSYSMENU
)
3661 /* prevent sysmenu activation for managed windows on Alt down/up */
3662 // if (GetPropA( hwnd, "__wine_x11_managed" ))
3663 wFlags
|= TF_ENDMENU
; /* schedule end of menu tracking */
3667 if( uItem
== NO_SELECTED_ITEM
)
3668 MenuMoveSelection( hWnd
, &MenuInfo
, ITEM_NEXT
);
3670 PostMessageW( hWnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3674 MenuTrackMenu( hTrackMenu
, wFlags
, 0, 0, hWnd
, NULL
);
3675 MenuExitTracking( hWnd
);
3681 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3682 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3684 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3686 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3687 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3688 * MFT_STRING is replaced by MIIM_STRING.
3689 * (So, I guess we should use MIIM_STRING only for strings?)
3691 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3693 * Based on wine, SetMenuItemInfo_common:
3694 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3695 * it will result in a error.
3696 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3697 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3704 LPMENUITEMINFOW mii
,
3711 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3713 if(Flags
& MF_BITMAP
)
3715 mii
->fMask
|= MIIM_BITMAP
; /* Use the new way of seting hbmpItem.*/
3716 mii
->hbmpItem
= (HBITMAP
) NewItem
;
3718 if (Flags
& MF_HELP
)
3720 /* increase ident */
3721 mii
->fType
|= MF_HELP
;
3724 else if(Flags
& MF_OWNERDRAW
)
3726 mii
->fType
|= MFT_OWNERDRAW
;
3727 mii
->fMask
|= MIIM_DATA
;
3728 mii
->dwItemData
= (DWORD
) NewItem
;
3730 else if (Flags
& MF_SEPARATOR
)
3732 mii
->fType
|= MFT_SEPARATOR
;
3733 if (!(Flags
& (MF_GRAYED
|MF_DISABLED
)))
3734 Flags
|= MF_GRAYED
|MF_DISABLED
;
3736 else /* Default action MF_STRING. */
3738 /* Item beginning with a backspace is a help item */
3739 if (NewItem
!= NULL
)
3743 if (*NewItem
== '\b')
3745 mii
->fType
|= MF_HELP
;
3751 LPCSTR NewItemA
= (LPCSTR
) NewItem
;
3752 if (*NewItemA
== '\b')
3754 mii
->fType
|= MF_HELP
;
3756 NewItem
= (LPCWSTR
) NewItemA
;
3760 if (Flags
& MF_HELP
)
3761 mii
->fType
|= MF_HELP
;
3762 mii
->fMask
|= MIIM_STRING
;
3763 mii
->fType
|= MFT_STRING
; /* Zero */
3764 mii
->dwTypeData
= (LPWSTR
)NewItem
;
3766 mii
->cch
= (NULL
== NewItem
? 0 : strlenW(NewItem
));
3768 mii
->cch
= (NULL
== NewItem
? 0 : strlen((LPCSTR
)NewItem
));
3772 mii
->fType
|= MFT_SEPARATOR
;
3773 if (!(Flags
& (MF_GRAYED
|MF_DISABLED
)))
3774 Flags
|= MF_GRAYED
|MF_DISABLED
;
3778 if(Flags
& MF_RIGHTJUSTIFY
) /* Same as MF_HELP */
3780 mii
->fType
|= MFT_RIGHTJUSTIFY
;
3783 if(Flags
& MF_MENUBREAK
)
3785 mii
->fType
|= MFT_MENUBREAK
;
3787 else if(Flags
& MF_MENUBARBREAK
)
3789 mii
->fType
|= MFT_MENUBARBREAK
;
3792 if(Flags
& MF_GRAYED
|| Flags
& MF_DISABLED
)
3794 if (Flags
& MF_GRAYED
)
3795 mii
->fState
|= MF_GRAYED
;
3797 if (Flags
& MF_DISABLED
)
3798 mii
->fState
|= MF_DISABLED
;
3800 mii
->fMask
|= MIIM_STATE
;
3802 else if (Flags
& MF_HILITE
)
3804 mii
->fState
|= MF_HILITE
;
3805 mii
->fMask
|= MIIM_STATE
;
3807 else /* default state */
3809 mii
->fState
|= MFS_ENABLED
;
3810 mii
->fMask
|= MIIM_STATE
;
3813 if(Flags
& MF_POPUP
)
3815 mii
->fType
|= MF_POPUP
;
3816 mii
->fMask
|= MIIM_SUBMENU
;
3817 mii
->hSubMenu
= (HMENU
)IDNewItem
;
3821 mii
->fMask
|= MIIM_ID
;
3822 mii
->wID
= (UINT
)IDNewItem
;
3828 /* FUNCTIONS *****************************************************************/
3831 MenuIsStringItem(ULONG TypeData)
3833 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3841 AppendMenuA(HMENU hMenu
,
3843 UINT_PTR uIDNewItem
,
3846 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3855 AppendMenuW(HMENU hMenu
,
3857 UINT_PTR uIDNewItem
,
3860 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3869 CheckMenuItem(HMENU hmenu
,
3873 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3878 MenuCheckMenuRadioItem(HMENU hMenu
, UINT idFirst
, UINT idLast
, UINT idCheck
, UINT uFlags
, BOOL bCheck
, PUINT pChecked
, PUINT pUnchecked
, PUINT pMenuChanged
)
3881 PROSMENUITEMINFO Items
= NULL
;
3882 UINT cChecked
, cUnchecked
;
3886 if(idFirst
> idLast
)
3889 ItemCount
= GetMenuItemCount(hMenu
);
3891 //mi.cbSize = sizeof(ROSMENUINFO);
3892 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3895 if(MenuGetAllRosMenuItemInfo(hMenu
, &Items
) <= 0)
3897 ERR("MenuGetAllRosMenuItemInfo failed\n");
3901 cChecked
= cUnchecked
= 0;
3903 for (i
= 0 ; i
< ItemCount
; i
++)
3906 if (0 != (Items
[i
].fType
& MF_MENUBARBREAK
)) continue;
3907 if (0 != (Items
[i
].fType
& MF_SEPARATOR
)) continue;
3909 if ((Items
[i
].fType
& MF_POPUP
) && (uFlags
== MF_BYCOMMAND
))
3911 MenuCheckMenuRadioItem(Items
[i
].hSubMenu
, idFirst
, idLast
, idCheck
, uFlags
, bCheck
, pChecked
, pUnchecked
, pMenuChanged
);
3914 if (uFlags
& MF_BYPOSITION
)
3916 if (i
< idFirst
|| i
> idLast
)
3931 if (Items
[i
].wID
< idFirst
|| Items
[i
].wID
> idLast
)
3934 if (Items
[i
].wID
== idCheck
)
3948 Items
[i
].fMask
= MIIM_STATE
| MIIM_FTYPE
;
3951 Items
[i
].fType
|= MFT_RADIOCHECK
;
3952 Items
[i
].fState
|= MFS_CHECKED
;
3956 Items
[i
].fState
&= ~MFS_CHECKED
;
3959 if(!MenuSetRosMenuItemInfo(hMenu
, i
,&Items
[i
]))
3961 ERR("MenuSetRosMenuItemInfo failed\n");
3966 HeapFree(GetProcessHeap(), 0, Items
);
3968 *pChecked
+= cChecked
;
3969 *pUnchecked
+= cUnchecked
;
3971 if (cChecked
|| cUnchecked
)
3981 CheckMenuRadioItem(HMENU hmenu
,
3988 UINT cUnchecked
= 0;
3989 UINT cMenuChanged
= 0;
3991 if (!MenuCheckMenuRadioItem(hmenu
, idFirst
, idLast
, idCheck
, uFlags
, FALSE
, &cChecked
, &cUnchecked
, &cMenuChanged
))
3994 if (cMenuChanged
> 1)
4001 if (!MenuCheckMenuRadioItem(hmenu
, idFirst
, idLast
, idCheck
, uFlags
, TRUE
, &cChecked
, &cUnchecked
, &cMenuChanged
))
4004 return (cChecked
!= 0);
4015 return (HMENU
)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU
);
4023 CreatePopupMenu(VOID
)
4026 return (HMENU
)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP
);
4034 DrawMenuBar(HWND hWnd
)
4036 ROSMENUINFO MenuInfo
;
4038 hMenu
= GetMenu(hWnd
);
4041 MenuGetRosMenuInfo(&MenuInfo
, hMenu
);
4042 MenuInfo
.Height
= 0; // make sure to recalc size
4043 MenuSetRosMenuInfo(&MenuInfo
);
4044 /* The wine method doesn't work and I suspect it's more effort
4045 then hackfix solution
4046 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4047 SWP_NOZORDER | SWP_FRAMECHANGED );
4050 return SendMessage(hWnd
,WM_NCPAINT
,(WPARAM
)1,(LPARAM
)0L);
4058 EnableMenuItem(HMENU hMenu
,
4062 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
4072 guii
.cbSize
= sizeof(GUITHREADINFO
);
4073 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
4075 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
4087 return NtUserGetMenu(hWnd
);
4095 GetMenuCheckMarkDimensions(VOID
)
4097 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
4098 GetSystemMetrics(SM_CYMENUCHECK
)));
4106 GetMenuDefaultItem(HMENU hMenu
,
4110 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
4118 GetMenuInfo(HMENU hmenu
,
4124 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
4127 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
4128 mi
.cbSize
= sizeof(MENUINFO
);
4129 mi
.fMask
= lpcmi
->fMask
;
4131 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
4133 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
4142 GetMenuItemCount(HMENU Menu
)
4144 ROSMENUINFO MenuInfo
;
4146 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
4154 GetMenuItemID(HMENU hMenu
,
4157 ROSMENUITEMINFO mii
;
4159 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4160 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
4162 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
4167 if (NULL
!= mii
.hSubMenu
)
4188 LPMENUITEMINFOA mii
)
4194 if (mii
->cbSize
!= sizeof(MENUITEMINFOA
) &&
4195 mii
->cbSize
!= sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
))
4197 SetLastError(ERROR_INVALID_PARAMETER
);
4201 if(!(mii
->fMask
& (MIIM_TYPE
| MIIM_STRING
)))
4203 /* No text requested, just pass on */
4204 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
4207 AnsiBuffer
= mii
->dwTypeData
;
4208 Count
= miiW
.cch
= mii
->cch
;
4209 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
4210 miiW
.dwTypeData
= 0;
4214 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
4215 miiW
.cch
* sizeof(WCHAR
));
4216 if (miiW
.dwTypeData
== NULL
) return FALSE
;
4217 miiW
.dwTypeData
[0] = 0;
4220 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)&miiW
, FALSE
))
4222 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4226 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
);
4228 if (!AnsiBuffer
|| !Count
)
4230 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4231 mii
->dwTypeData
= AnsiBuffer
;
4232 mii
->cch
= miiW
.cch
;
4236 if ((miiW
.fMask
& MIIM_STRING
) || (IS_STRING_ITEM(miiW
.fType
)))
4240 if (!WideCharToMultiByte(CP_ACP
, 0, miiW
.dwTypeData
, miiW
.cch
, AnsiBuffer
, mii
->cch
, NULL
, NULL
))
4244 if (Count
> miiW
.cch
)
4246 AnsiBuffer
[miiW
.cch
] = 0;
4248 mii
->cch
= mii
->cch
;
4256 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4257 mii
->dwTypeData
= AnsiBuffer
;
4271 LPMENUITEMINFOW mii
)
4277 if (mii
->cbSize
!= sizeof(MENUITEMINFOW
) &&
4278 mii
->cbSize
!= sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
))
4280 SetLastError(ERROR_INVALID_PARAMETER
);
4284 if(!(mii
->fMask
& (MIIM_TYPE
| MIIM_STRING
)))
4286 /* No text requested, just pass on */
4287 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
4290 String
= mii
->dwTypeData
;
4292 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
4293 miiW
.dwTypeData
= 0;
4297 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
4298 miiW
.cch
* sizeof(WCHAR
));
4299 if (miiW
.dwTypeData
== NULL
) return FALSE
;
4300 miiW
.dwTypeData
[0] = 0;
4303 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) &miiW
, FALSE
))
4305 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4309 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
); // Okay to over write user data.
4311 if (!String
|| !Count
)
4313 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4314 mii
->dwTypeData
= String
; // may not be zero.
4315 mii
->cch
= miiW
.cch
;
4319 if ((miiW
.fMask
& MIIM_STRING
) || (IS_STRING_ITEM(miiW
.fType
)))
4321 lstrcpynW( String
, miiW
.dwTypeData
, Count
);
4324 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4325 mii
->dwTypeData
= String
;
4326 mii
->cch
= strlenW(String
);
4341 ROSMENUINFO MenuInfo
;
4342 ROSMENUITEMINFO mii
;
4343 memset( &mii
, 0, sizeof(mii
) );
4344 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4345 mii
.fMask
= MIIM_STATE
| MIIM_FTYPE
| MIIM_SUBMENU
;
4348 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
4353 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
4357 nSubItems
= MenuInfo
.MenuItemCount
;
4359 /* FIXME - ported from wine, does that work (0xff)? */
4360 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
4361 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
4363 return (UINT
)-1; /* Invalid submenu */
4366 /* FIXME - ported from wine, does that work? */
4367 return (mii
.fType
| mii
.fState
);
4387 memset( &mii
, 0, sizeof(mii
) );
4388 mii
.dwTypeData
= lpString
;
4389 mii
.fMask
= MIIM_STRING
| MIIM_FTYPE
;
4390 mii
.fType
= MFT_STRING
;
4391 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4392 mii
.cch
= nMaxCount
;
4394 if(!(GetMenuItemInfoA( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&mii
)))
4414 memset( &miiW
, 0, sizeof(miiW
) );
4415 miiW
.dwTypeData
= lpString
;
4416 miiW
.fMask
= MIIM_STRING
| MIIM_FTYPE
;
4417 miiW
.fType
= MFT_STRING
;
4418 miiW
.cbSize
= sizeof(MENUITEMINFOW
);
4419 miiW
.cch
= nMaxCount
;
4421 if(!(GetMenuItemInfoW( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&miiW
)))
4439 mi
.cbSize
= sizeof(MENUITEMINFOW
);
4440 mi
.fMask
= MIIM_SUBMENU
;
4442 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
4444 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
4461 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
4463 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
4476 UINT_PTR uIDNewItem
,
4480 memset( &mii
, 0, sizeof(mii
) );
4481 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4482 mii
.fMask
= MIIM_FTYPE
;
4484 MenuSetItemData((LPMENUITEMINFOW
) &mii
,
4487 (LPCWSTR
) lpNewItem
,
4490 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4504 LPCMENUITEMINFOA lpmii
)
4507 UNICODE_STRING MenuText
;
4509 BOOL CleanHeap
= FALSE
;
4512 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4513 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4515 RtlCopyMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4517 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4519 mi
.cbSize
= sizeof( MENUITEMINFOW
);
4522 /* copy the text string */
4523 if (((mi
.fMask
& MIIM_STRING
) ||
4524 ((mi
.fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
)))
4525 && mi
.dwTypeData
!= NULL
)
4527 Status
= RtlCreateUnicodeStringFromAsciiz(&MenuText
, (LPSTR
)mi
.dwTypeData
);
4528 if (!NT_SUCCESS (Status
))
4530 SetLastError (RtlNtStatusToDosError(Status
));
4533 mi
.dwTypeData
= MenuText
.Buffer
;
4534 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4537 res
= NtUserThunkedMenuItemInfo(hMenu
, uItem
, fByPosition
, TRUE
, &mi
, NULL
);
4539 if ( CleanHeap
) RtlFreeUnicodeString ( &MenuText
);
4554 LPCMENUITEMINFOW lpmii
)
4557 UNICODE_STRING MenuText
;
4560 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4561 if a bad user passes bad data, we crash his process instead of the
4564 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4565 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4567 RtlCopyMemory(&mi
, lpmii
, lpmii
->cbSize
);
4569 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4571 mi
.cbSize
= sizeof( MENUITEMINFOW
);
4574 /* copy the text string */
4575 if (((mi
.fMask
& MIIM_STRING
) ||
4576 ((mi
.fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
)))
4577 && mi
.dwTypeData
!= NULL
)
4579 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4580 mi
.dwTypeData
= MenuText
.Buffer
;
4581 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4583 res
= NtUserThunkedMenuItemInfo(hMenu
, uItem
, fByPosition
, TRUE
, &mi
, NULL
);
4598 UINT_PTR uIDNewItem
,
4602 memset( &mii
, 0, sizeof(mii
) );
4603 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4604 mii
.fMask
= MIIM_FTYPE
;
4606 MenuSetItemData( &mii
,
4612 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4624 ROSMENUINFO MenuInfo
;
4626 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4634 LoadMenuA(HINSTANCE hInstance
,
4637 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4638 if (Resource
== NULL
)
4642 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4650 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4652 return(LoadMenuIndirectW(lpMenuTemplate
));
4660 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4663 WORD version
, offset
;
4664 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4666 version
= GET_WORD(p
);
4671 case 0: /* standard format is version of 0 */
4672 offset
= GET_WORD(p
);
4673 p
+= sizeof(WORD
) + offset
;
4674 if (!(hMenu
= CreateMenu())) return 0;
4675 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4681 case 1: /* extended format is version of 1 */
4682 offset
= GET_WORD(p
);
4683 p
+= sizeof(WORD
) + offset
;
4684 if (!(hMenu
= CreateMenu())) return 0;
4685 if (!MENUEX_ParseResource(p
, hMenu
))
4687 DestroyMenu( hMenu
);
4692 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4702 LoadMenuW(HINSTANCE hInstance
,
4705 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4706 if (Resource
== NULL
)
4710 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4724 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4737 UINT_PTR uIDNewItem
,
4741 ROSMENUITEMINFO rmii
;
4743 memset( &mii
, 0, sizeof(mii
) );
4744 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4745 mii
.fMask
= MIIM_FTYPE
;
4747 if (!MenuGetRosMenuInfo( &mi
, hMnu
)) return FALSE
;
4751 if (!MenuSetRosMenuInfo( &mi
)) return FALSE
;
4753 MenuInitRosMenuItemInfo( &rmii
);
4755 if(!MenuGetRosMenuItemInfo( hMnu
, uPosition
, &rmii
)) return FALSE
;
4757 if ((rmii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (rmii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4758 NtUserDestroyMenu( rmii
.hSubMenu
); /* ModifyMenu() spec */
4760 MenuCleanupRosMenuItemInfo( &rmii
);
4762 MenuSetItemData((LPMENUITEMINFOW
) &mii
,
4765 (LPCWSTR
) lpNewItem
,
4768 return SetMenuItemInfoA( hMnu
,
4770 (BOOL
)(MF_BYPOSITION
& uFlags
),
4784 UINT_PTR uIDNewItem
,
4788 ROSMENUITEMINFO rmii
;
4790 memset ( &mii
, 0, sizeof(mii
) );
4791 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4792 mii
.fMask
= MIIM_FTYPE
;
4794 if (!MenuGetRosMenuInfo( &mi
, hMnu
)) return FALSE
;
4796 mi
.Height
= 0; // Force size recalculation.
4798 if (!MenuSetRosMenuInfo( &mi
)) return FALSE
;
4800 MenuInitRosMenuItemInfo( &rmii
);
4802 if(!MenuGetRosMenuItemInfo( hMnu
, uPosition
, &rmii
)) return FALSE
;
4804 if ((rmii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (rmii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4805 NtUserDestroyMenu( rmii
.hSubMenu
); /* ModifyMenu() spec */
4807 MenuCleanupRosMenuItemInfo( &rmii
);
4809 /* Init new data for this menu item */
4810 MenuSetItemData( &mii
,
4816 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4817 return SetMenuItemInfoW( hMnu
,
4819 (BOOL
)(MF_BYPOSITION
& uFlags
),
4831 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4846 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4849 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4850 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4863 HBITMAP hBitmapUnchecked
,
4864 HBITMAP hBitmapChecked
)
4866 ROSMENUITEMINFO uItem
;
4867 memset ( &uItem
, 0, sizeof(uItem
) );
4868 uItem
.fMask
= MIIM_STATE
| MIIM_BITMAP
;
4870 if(!(NtUserMenuItemInfo(hMenu
, uPosition
,
4871 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, FALSE
))) return FALSE
;
4873 if (!hBitmapChecked
&& !hBitmapUnchecked
)
4875 uItem
.fState
&= ~MF_USECHECKBITMAPS
;
4877 else /* Install new bitmaps */
4879 uItem
.hbmpChecked
= hBitmapChecked
;
4880 uItem
.hbmpUnchecked
= hBitmapUnchecked
;
4881 uItem
.fState
|= MF_USECHECKBITMAPS
;
4883 return NtUserMenuItemInfo(hMenu
, uPosition
,
4884 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, TRUE
);
4897 LPCMENUITEMINFOA lpmii
)
4899 MENUITEMINFOW MenuItemInfoW
;
4900 UNICODE_STRING UnicodeString
;
4902 ULONG Result
= FALSE
;
4904 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4906 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4908 MenuItemInfoW
.cbSize
= sizeof( MENUITEMINFOW
);
4909 MenuItemInfoW
.hbmpItem
= NULL
;
4912 * MIIM_STRING == good
4913 * MIIM_TYPE & MFT_STRING == good
4914 * MIIM_STRING & MFT_STRING == good
4915 * MIIM_STRING & MFT_OWNERSRAW == good
4917 if (((MenuItemInfoW
.fMask
& MIIM_STRING
) ||
4918 ((MenuItemInfoW
.fMask
& MIIM_TYPE
) &&
4919 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
)))
4920 && MenuItemInfoW
.dwTypeData
!= NULL
)
4922 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
4923 Status
= RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4924 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4925 if (!NT_SUCCESS (Status
))
4927 SetLastError (RtlNtStatusToDosError(Status
));
4930 MenuItemInfoW
.dwTypeData
= UnicodeString
.Buffer
;
4931 MenuItemInfoW
.cch
= UnicodeString
.Length
/ sizeof(WCHAR
);
4935 UnicodeString
.Buffer
= NULL
;
4938 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4939 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4941 if (UnicodeString
.Buffer
!= NULL
)
4943 RtlFreeUnicodeString(&UnicodeString
);
4959 LPCMENUITEMINFOW lpmii
)
4961 MENUITEMINFOW MenuItemInfoW
;
4964 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4966 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4968 MenuItemInfoW
.cbSize
= sizeof( MENUITEMINFOW
);
4969 MenuItemInfoW
.hbmpItem
= NULL
;
4972 if (((MenuItemInfoW
.fMask
& MIIM_STRING
) ||
4973 ((MenuItemInfoW
.fMask
& MIIM_TYPE
) &&
4974 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
)))
4975 && MenuItemInfoW
.dwTypeData
!= NULL
)
4977 MenuItemInfoW
.cch
= strlenW(MenuItemInfoW
.dwTypeData
);
4979 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4980 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4996 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
5001 SetLastError(ERROR_INVALID_MENU_HANDLE
);
5004 return NtUserSetSystemMenu(hwnd
, hMenu
);
5024 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
5026 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
5027 if (0 == (Flags
& TPM_NONOTIFY
))
5029 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
5032 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
5034 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
5036 MenuExitTracking(Wnd
);
5055 /* Not fully implemented */
5056 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
5057 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
5061 // Example for the Win32/User32 rewrite.
5062 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5076 return NtUserTrackPopupMenuEx( Menu
,
5081 NULL
); // LPTPMPARAMS is null
5090 GetMenuContextHelpId(HMENU hmenu
)
5093 mi
.cbSize
= sizeof(ROSMENUINFO
);
5094 mi
.fMask
= MIM_HELPID
;
5096 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
5098 return mi
.dwContextHelpID
;
5143 LPCWSTR lpszNewItem
,
5148 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5149 for MF_DELETE. We should check the parameters for all others
5150 MF_* actions also (anybody got a doc on ChangeMenu?).
5153 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
5156 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
5159 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
5162 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
5165 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
5166 flags
&~ MF_REMOVE
);
5168 default : /* MF_INSERT */
5169 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
5186 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5187 for MF_DELETE. We should check the parameters for all others
5188 MF_* actions also (anybody got a doc on ChangeMenu?).
5191 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
5194 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
5197 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
5200 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
5203 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
5204 flags
&~ MF_REMOVE
);
5206 default : /* MF_INSERT */
5207 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);