3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * Partly based on Wine code
6 * Copyright 1993 Martin Ayotte
7 * Copyright 1994 Alexandre Julliard
8 * Copyright 1997 Morten Welinder
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * PROJECT: ReactOS user32.dll
27 * FILE: lib/user32/windows/menu.c
29 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
31 * 09-05-2001 CSH Created
34 /* INCLUDES ******************************************************************/
37 #include "../controls/controls.h"
39 /* internal popup menu window messages */
40 #define MM_SETMENUHANDLE (WM_USER + 0)
41 #define MM_GETMENUHANDLE (WM_USER + 1)
43 /* Internal MenuTrackMenu() flags */
44 #define TPM_INTERNAL 0xF0000000
45 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
46 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
47 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
49 /* TYPES *********************************************************************/
51 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
53 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
54 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
55 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
57 #define IS_SYSTEM_MENU(MenuInfo) \
58 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
60 #define IS_SYSTEM_POPUP(MenuInfo) \
61 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
63 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
65 #define MENU_BAR_ITEMS_SPACE (12)
66 #define SEPARATOR_HEIGHT (5)
67 #define MENU_TAB_SPACE (8)
73 #define MF_END (0x0080)
77 #define MIIM_STRING (0x00000040)
80 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
81 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
82 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
83 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
85 /* internal flags for menu tracking */
87 #define TF_ENDMENU 0x0001
88 #define TF_SUSPENDPOPUP 0x0002
89 #define TF_SKIPREMOVE 0x0004
94 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
95 HMENU TopMenu
; /* initial menu */
96 HWND OwnerWnd
; /* where notifications are sent */
100 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
102 /*********************************************************************
103 * PopupMenu class descriptor
105 const struct builtin_class_descr POPUPMENU_builtin_class
=
107 POPUPMENU_CLASS_ATOMW
, /* name */
108 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
109 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
110 (WNDPROC
) NULL
, /* FIXME - procA */
111 sizeof(MENUINFO
*), /* extra */
112 (LPCWSTR
) IDC_ARROW
, /* cursor */
113 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
117 /* INTERNAL FUNCTIONS ********************************************************/
119 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
120 * Of course I didnt copy the ASM code because we want this to be portable
121 * and it needs to go away.
125 #define GET_WORD(ptr) (*(WORD *)(ptr))
128 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
131 HFONT hMenuFont
= NULL
;
132 HFONT hMenuFontBold
= NULL
;
134 /* Flag set by EndMenu() to force an exit from menu tracking */
135 static BOOL fEndMenu
= FALSE
;
137 /* Use global popup window because there's no way 2 menus can
138 * be tracked at the same time. */
139 static HWND TopPopup
;
141 /* Dimension of the menu bitmaps */
142 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
144 static HBITMAP StdMnArrow
= NULL
;
145 static HBITMAP BmpSysMenu
= NULL
;
147 /***********************************************************************
150 * Get full information about menu
153 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
155 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
156 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
158 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
161 /***********************************************************************
164 * Set full information about menu
167 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
169 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
170 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
172 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
175 /***********************************************************************
176 * MenuInitRosMenuItemInfo
178 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
181 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
183 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
184 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
187 /***********************************************************************
188 * MenuGetRosMenuItemInfo
190 * Get full information about a menu item
193 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
195 if (ItemInfo
->dwTypeData
!= NULL
)
197 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
200 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
201 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
202 ItemInfo
->dwTypeData
= NULL
;
204 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
210 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
)
213 ItemInfo
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0,
214 ItemInfo
->cch
* sizeof(WCHAR
));
215 if (NULL
== ItemInfo
->dwTypeData
)
220 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
230 /***********************************************************************
231 * MenuSetRosMenuItemInfo
233 * Set full information about a menu item
236 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
240 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
&&
241 ItemInfo
->dwTypeData
!= NULL
)
243 ItemInfo
->cch
= wcslen(ItemInfo
->dwTypeData
);
245 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
246 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
249 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
254 /***********************************************************************
255 * MenuCleanupRosMenuItemInfo
257 * Cleanup after use of MenuGet/SetRosMenuItemInfo
260 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
262 if (ItemInfo
->dwTypeData
!= NULL
)
264 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
268 /***********************************************************************
269 * MenuGetAllRosMenuItemInfo
271 * Get full information about all menu items
274 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
278 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
283 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
284 if (NULL
== *ItemInfo
)
289 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
292 /***********************************************************************
293 * MenuCleanupAllRosMenuItemInfo
295 * Cleanup after use of MenuGetAllRosMenuItemInfo
298 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
300 HeapFree(GetProcessHeap(), 0, ItemInfo
);
304 /***********************************************************************
307 * Load the arrow bitmap. We can't do this from MenuInit since user32
308 * can also be used (and thus initialized) from text-mode.
311 MenuLoadBitmaps(VOID
)
313 /* Load menu bitmaps */
314 if (NULL
== StdMnArrow
)
316 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
318 if (NULL
!= StdMnArrow
)
321 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
322 ArrowBitmapWidth
= bm
.bmWidth
;
323 ArrowBitmapHeight
= bm
.bmHeight
;
327 /* Load system buttons bitmaps */
328 if (NULL
== BmpSysMenu
)
330 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
334 /***********************************************************************
335 * MenuGetBitmapItemSize
337 * Get the size of a bitmap item.
340 MenuGetBitmapItemSize(UINT Id
, DWORD Data
, SIZE
*Size
)
343 HBITMAP Bmp
= (HBITMAP
) Id
;
345 Size
->cx
= Size
->cy
= 0;
347 /* check if there is a magic menu item associated with this item */
348 if (0 != Id
&& IS_MAGIC_ITEM(Id
))
350 switch((INT_PTR
) LOWORD(Id
))
352 case (INT_PTR
) HBMMENU_SYSTEM
:
355 Bmp
= (HBITMAP
) Data
;
359 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
360 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
361 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
362 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
363 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
364 /* FIXME: Why we need to subtract these magic values? */
365 Size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
366 Size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
368 case (INT_PTR
) HBMMENU_CALLBACK
:
369 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
370 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
371 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
372 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
374 DPRINT("Magic menu bitmap not implemented\n");
379 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
381 Size
->cx
= Bm
.bmWidth
;
382 Size
->cy
= Bm
.bmHeight
;
386 /***********************************************************************
389 * Draw a bitmap item.
392 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
, BOOL MenuBar
)
397 HBITMAP Bmp
= (HBITMAP
) Item
->hbmpItem
;
398 int w
= Rect
->right
- Rect
->left
;
399 int h
= Rect
->bottom
- Rect
->top
;
403 /* Check if there is a magic menu item associated with this item */
404 if (IS_MAGIC_ITEM(Item
->hbmpItem
))
410 switch ((int) Item
->hbmpItem
)
412 case (INT_PTR
) HBMMENU_SYSTEM
:
413 if (NULL
!= Item
->hbmpItem
)
415 Bmp
= Item
->hbmpItem
;
416 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
424 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
428 /* only use right half of the bitmap */
429 BmpXoffset
= Bm
.bmWidth
/ 2;
430 Bm
.bmWidth
-= BmpXoffset
;
433 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
434 Flags
= DFCS_CAPTIONRESTORE
;
436 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
438 Flags
= DFCS_CAPTIONMIN
;
440 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
442 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
444 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
445 Flags
= DFCS_CAPTIONCLOSE
;
447 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
448 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
450 case (INT_PTR
) HBMMENU_CALLBACK
:
451 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
452 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
453 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
454 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
456 DPRINT("Magic menu bitmap not implemented\n");
459 InflateRect(&r
, -1, -1);
460 if (0 != (Item
->fState
& MF_HILITE
))
462 Flags
|= DFCS_PUSHED
;
464 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
468 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
474 DcMem
= CreateCompatibleDC(Dc
);
475 SelectObject(DcMem
, Bmp
);
477 /* handle fontsize > bitmap_height */
478 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
480 Rop
= (0 != (Item
->fState
& MF_HILITE
) && ! IS_MAGIC_ITEM(Item
->hbmpItem
)) ? NOTSRCCOPY
: SRCCOPY
;
481 if (0 != (Item
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(Item
->fType
))
483 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
485 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
489 /***********************************************************************
492 * Draw a single menu item.
495 MenuDrawMenuItem(HWND Wnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
496 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
501 if (0 != (Item
->fType
& MF_SYSMENU
))
505 UserGetInsideRectNC(Wnd
, &Rect
);
506 UserDrawSysMenuButton(Wnd
, Dc
, &Rect
,
507 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
515 if (0 != (Item
->fState
& MF_HILITE
))
519 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
520 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
524 if (0 != (Item
->fState
& MF_GRAYED
))
526 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
530 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
532 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
537 if (0 != (Item
->fState
& MF_GRAYED
))
539 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
543 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
545 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
548 if (0 != (Item
->fType
& MF_OWNERDRAW
))
551 ** Experimentation under Windows reveals that an owner-drawn
552 ** menu is given the rectangle which includes the space it requested
553 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
554 ** and a popup-menu arrow. This is the value of lpitem->rect.
555 ** Windows will leave all drawing to the application except for
556 ** the popup-menu arrow. Windows always draws that itself, after
557 ** the menu owner has finished drawing.
561 dis
.CtlType
= ODT_MENU
;
563 dis
.itemID
= Item
->wID
;
564 dis
.itemData
= (DWORD
)Item
->dwItemData
;
566 if (0 != (Item
->fState
& MF_CHECKED
))
568 dis
.itemState
|= ODS_CHECKED
;
570 if (0 != (Item
->fState
& MF_GRAYED
))
572 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
574 if (0 != (Item
->fState
& MF_HILITE
))
576 dis
.itemState
|= ODS_SELECTED
;
578 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
579 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
581 dis
.rcItem
= Item
->Rect
;
582 DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
583 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
584 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
585 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
587 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
588 /* Fall through to draw popup-menu arrow */
591 DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
592 Item
->Rect
.right
, Item
->Rect
.bottom
);
594 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
601 if (0 == (Item
->fType
& MF_OWNERDRAW
))
603 if (Item
->fState
& MF_HILITE
)
607 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
611 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
616 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENU
));
620 SetBkMode(Dc
, TRANSPARENT
);
622 if (0 == (Item
->fType
& MF_OWNERDRAW
))
624 /* vertical separator */
625 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
629 rc
.bottom
= Height
- 3;
630 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
633 /* horizontal separator */
634 if (0 != (Item
->fType
& MF_SEPARATOR
))
639 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
640 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
647 /* helper lines for debugging */
648 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
649 SelectObject(Dc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
));
650 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
651 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
656 INT y
= Rect
.top
+ Rect
.bottom
;
657 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
658 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
660 if (0 == (Item
->fType
& MF_OWNERDRAW
))
662 /* Draw the check mark
665 * Custom checkmark bitmaps are monochrome but not always 1bpp.
667 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
668 if (NULL
!= bm
) /* we have a custom bitmap */
670 HDC DcMem
= CreateCompatibleDC(Dc
);
671 SelectObject(DcMem
, bm
);
672 BitBlt(Dc
, Rect
.left
, (y
- CheckBitmapHeight
) / 2,
673 CheckBitmapWidth
, CheckBitmapHeight
,
674 DcMem
, 0, 0, SRCCOPY
);
677 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
680 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
681 HDC DcMem
= CreateCompatibleDC(Dc
);
682 SelectObject(DcMem
, bm
);
683 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
684 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
685 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
686 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
687 BitBlt(Dc
, Rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
688 DcMem
, 0, 0, SRCCOPY
);
694 /* Draw the popup-menu arrow */
695 if (0 != (Item
->fType
& MF_POPUP
))
697 HDC DcMem
= CreateCompatibleDC(Dc
);
700 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
701 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
702 (y
- ArrowBitmapHeight
) / 2,
703 ArrowBitmapWidth
, ArrowBitmapHeight
,
704 DcMem
, 0, 0, SRCCOPY
);
705 SelectObject(DcMem
, OrigBitmap
);
709 Rect
.left
+= CheckBitmapWidth
;
710 Rect
.right
-= ArrowBitmapWidth
;
713 /* Done for owner-drawn */
714 if (0 != (Item
->fType
& MF_OWNERDRAW
))
719 /* Draw the item text or bitmap */
720 if (IS_BITMAP_ITEM(Item
->fType
))
722 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuBar
);
725 /* No bitmap - process text if present */
726 else if (IS_STRING_ITEM(Item
->fType
))
729 HFONT FontOld
= NULL
;
731 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
732 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
734 if (0 != (Item
->fState
& MFS_DEFAULT
))
736 FontOld
= SelectObject(Dc
, hMenuFontBold
);
741 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
742 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
745 Text
= (PWCHAR
) Item
->dwTypeData
;
746 for (i
= 0; L
'\0' != Text
[i
]; i
++)
748 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
754 if (0 != (Item
->fState
& MF_GRAYED
))
756 if (0 == (Item
->fState
& MF_HILITE
))
758 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
759 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
760 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
761 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
763 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
766 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
768 /* paint the shortcut text */
769 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
771 if (L
'\t' == Text
[i
])
773 Rect
.left
= Item
->XTab
;
774 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
778 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
781 if (0 != (Item
->fState
& MF_GRAYED
))
783 if (0 == (Item
->fState
& MF_HILITE
))
785 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
786 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
787 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
788 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
790 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
792 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
797 SelectObject(Dc
, FontOld
);
802 /***********************************************************************
805 * Paint a popup menu.
808 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
810 HBRUSH PrevBrush
= NULL
;
813 ROSMENUINFO MenuInfo
;
814 ROSMENUITEMINFO ItemInfo
;
817 DPRINT("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
819 GetClientRect(Wnd
, &Rect
);
821 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
822 && NULL
!= SelectObject(Dc
, hMenuFont
))
824 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
826 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
829 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
831 /* draw menu items */
833 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
835 MenuInitRosMenuItemInfo(&ItemInfo
);
837 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
839 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
841 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
842 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
846 MenuCleanupRosMenuItemInfo(&ItemInfo
);
851 SelectObject(Dc
, PrevBrush
);
856 static LRESULT WINAPI
857 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
859 DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
865 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
866 SetWindowLongW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
870 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
871 return MA_NOACTIVATE
;
876 BeginPaint(Wnd
, &ps
);
877 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongW(Wnd
, 0));
886 /* zero out global pointer in case resident popup window was destroyed. */
896 if (0 == GetWindowLongW(Wnd
, 0))
898 OutputDebugStringA("no menu to display\n");
903 SetWindowLongW(Wnd
, 0, 0);
907 case MM_SETMENUHANDLE
:
908 SetWindowLongW(Wnd
, 0, wParam
);
911 case MM_GETMENUHANDLE
:
912 return GetWindowLongW(Wnd
, 0);
915 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
921 /**********************************************************************
922 * MENUEX_ParseResource
924 * Parse an extended menu resource and add items to the menu.
925 * Return a pointer to the end of the resource.
927 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
929 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
937 mii
.cbSize
= sizeof(mii
);
938 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
939 mii
.fType
= GET_DWORD(res
);
940 res
+= sizeof(DWORD
);
941 mii
.fState
= GET_DWORD(res
);
942 res
+= sizeof(DWORD
);
943 mii
.wID
= GET_DWORD(res
);
944 res
+= sizeof(DWORD
);
945 resinfo
= GET_WORD(res
);
947 /* Align the text on a word boundary. */
948 res
+= (~((int)res
- 1)) & 1;
949 mii
.dwTypeData
= (LPWSTR
) res
;
950 res
+= (1 + wcslen(mii
.dwTypeData
)) * sizeof(WCHAR
);
951 /* Align the following fields on a dword boundary. */
952 res
+= (~((int)res
- 1)) & 3;
954 if (resinfo
& 1) /* Pop-up? */
956 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
957 res
+= sizeof(DWORD
);
958 mii
.hSubMenu
= CreatePopupMenu();
961 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
963 DestroyMenu(mii
.hSubMenu
);
966 mii
.fMask
|= MIIM_SUBMENU
;
967 mii
.fType
|= MF_POPUP
;
969 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
971 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
973 mii
.fType
|= MF_SEPARATOR
;
975 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
977 while (!(resinfo
& MF_END
));
982 /**********************************************************************
985 * Parse a standard menu resource and add items to the menu.
986 * Return a pointer to the end of the resource.
988 * NOTE: flags is equivalent to the mtOption field
990 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
999 flags
= GET_WORD(res
);
1001 /* remove MF_END flag before passing it to AppendMenu()! */
1002 end
= (flags
& MF_END
);
1003 if(end
) flags
^= MF_END
;
1005 res
+= sizeof(WORD
);
1006 if(!(flags
& MF_POPUP
))
1009 res
+= sizeof(WORD
);
1013 res
+= strlen(str
) + 1;
1015 res
+= (wcslen((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1016 if (flags
& MF_POPUP
)
1018 hSubMenu
= CreatePopupMenu();
1019 if(!hSubMenu
) return NULL
;
1020 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1023 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1025 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1027 else /* Not a popup */
1030 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1032 AppendMenuW(hMenu
, flags
, id
,
1033 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1042 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1046 hUser32
= GetModuleHandleW(L
"USER32");
1047 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
1048 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1055 NONCLIENTMETRICSW ncm
;
1057 /* get the menu font */
1058 if(!hMenuFont
|| !hMenuFontBold
)
1060 ncm
.cbSize
= sizeof(ncm
);
1061 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1063 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1067 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1068 if(hMenuFont
== NULL
)
1070 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1074 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1075 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1076 if(hMenuFontBold
== NULL
)
1078 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1087 /***********************************************************************
1090 * Calculate the size of the menu item and store it in ItemInfo->rect.
1092 static void FASTCALL
1093 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, HWND WndOwner
,
1094 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1097 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1099 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1101 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1103 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1106 ** Experimentation under Windows reveals that an owner-drawn
1107 ** menu is expected to return the size of the content part of
1108 ** the menu item, not including the checkmark nor the submenu
1109 ** arrow. Windows adds those values itself and returns the
1110 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1112 MEASUREITEMSTRUCT mis
;
1113 mis
.CtlType
= ODT_MENU
;
1115 mis
.itemID
= ItemInfo
->wID
;
1116 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1119 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1120 ItemInfo
->Rect
.right
+= mis
.itemWidth
;
1124 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1126 /* under at least win95 you seem to be given a standard
1127 height for the menu and the height value is ignored */
1129 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENU
) - 1;
1133 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1136 DPRINT("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1137 /* Fall through to get check/arrow width calculation. */
1140 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1142 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1148 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1149 if (0 != (ItemInfo
->fType
& MF_POPUP
))
1151 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1155 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1160 if (IS_BITMAP_ITEM(ItemInfo
->fType
))
1164 MenuGetBitmapItemSize((int) ItemInfo
->hbmpItem
, (DWORD
) ItemInfo
->hbmpItem
, &Size
);
1165 ItemInfo
->Rect
.right
+= Size
.cx
;
1166 ItemInfo
->Rect
.bottom
+= Size
.cy
;
1168 /* Leave space for the sunken border */
1169 ItemInfo
->Rect
.right
+= 2;
1170 ItemInfo
->Rect
.bottom
+= 2;
1172 /* Special case: Minimize button doesn't have a space behind it. */
1173 if (ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1174 ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1175 ItemInfo
->Rect
.right
-= 1;
1178 /* it must be a text item - unless it's the system menu */
1179 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && IS_STRING_ITEM(ItemInfo
->fType
))
1183 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1184 wcslen((LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1186 ItemInfo
->Rect
.right
+= Size
.cx
;
1187 ItemInfo
->Rect
.bottom
+= max(Size
.cy
, GetSystemMetrics(SM_CYMENU
) - 1);
1192 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1194 else if ((p
= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\t' )) != NULL
)
1196 /* Item contains a tab (only meaningful in popup menus) */
1197 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1198 (int)(p
- (LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1199 ItemInfo
->XTab
= CheckBitmapWidth
+ MENU_TAB_SPACE
+ Size
.cx
;
1200 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1204 if (NULL
!= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\b'))
1206 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1208 ItemInfo
->XTab
= ItemInfo
->Rect
.right
- CheckBitmapWidth
1213 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1216 /***********************************************************************
1217 * MenuPopupMenuCalcSize
1219 * Calculate the size of a popup menu.
1221 static void FASTCALL
1222 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1224 ROSMENUITEMINFO ItemInfo
;
1227 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1229 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1230 if (0 == MenuInfo
->MenuItemCount
)
1232 MenuSetRosMenuInfo(MenuInfo
);
1237 SelectObject(Dc
, hMenuFont
);
1242 MenuInitRosMenuItemInfo(&ItemInfo
);
1243 while (Start
< MenuInfo
->MenuItemCount
)
1248 MaxTab
= MaxTabWidth
= 0;
1250 /* Parse items until column break or end of menu */
1251 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1253 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1255 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1256 MenuSetRosMenuInfo(MenuInfo
);
1260 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1265 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1266 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1268 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1269 MenuSetRosMenuInfo(MenuInfo
);
1273 if (0 != (ItemInfo
.fType
& MF_MENUBARBREAK
))
1277 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1278 OrgY
= ItemInfo
.Rect
.bottom
;
1279 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1281 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1282 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1286 /* Finish the column (set all items to the largest width found) */
1287 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1290 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1292 ItemInfo
.Rect
.right
= MaxX
;
1293 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1295 ItemInfo
.XTab
= MaxTab
;
1297 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1301 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1304 MenuInfo
->Width
= MaxX
;
1306 /* space for 3d border */
1307 MenuInfo
->Height
+= 2;
1308 MenuInfo
->Width
+= 2;
1310 ReleaseDC(NULL
, Dc
);
1311 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1312 MenuSetRosMenuInfo(MenuInfo
);
1315 /***********************************************************************
1316 * MenuMenuBarCalcSize
1318 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1319 * height is off by 1 pixel which causes lengthy window relocations when
1320 * active document window is maximized/restored.
1322 * Calculate the size of the menu bar.
1324 static void FASTCALL
1325 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1327 ROSMENUITEMINFO ItemInfo
;
1328 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1330 if (NULL
== Rect
|| NULL
== MenuInfo
)
1334 if (0 == MenuInfo
->MenuItemCount
)
1339 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1340 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1341 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1342 MenuInfo
->Height
= 0;
1343 MaxY
= Rect
->top
+ 1;
1346 MenuInitRosMenuItemInfo(&ItemInfo
);
1347 while (Start
< MenuInfo
->MenuItemCount
)
1349 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1351 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1357 /* Parse items until line break or end of menu */
1358 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1360 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1365 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1370 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1371 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1372 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1374 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1378 if (ItemInfo
.Rect
.right
> Rect
->right
)
1386 ItemInfo
.Rect
.right
= Rect
->right
;
1389 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1390 OrgX
= ItemInfo
.Rect
.right
;
1391 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1393 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1395 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1401 /* FIXME: Is this really needed? */
1403 /* Finish the line (set all items to the largest height found) */
1406 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1408 ItemInfo
.Rect
.bottom
= MaxY
;
1409 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1418 Rect
->bottom
= MaxY
;
1419 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1420 MenuSetRosMenuInfo(MenuInfo
);
1424 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1425 /* the last item (if several lines, only move the last line) */
1426 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1428 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1431 OrgY
= ItemInfo
.Rect
.top
;
1433 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1439 if (ItemInfo
.Rect
.top
!= OrgY
)
1441 break; /* Other line */
1443 if (OrgX
<= ItemInfo
.Rect
.right
)
1445 break; /* Too far right already */
1447 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1448 ItemInfo
.Rect
.right
= OrgX
;
1449 OrgX
= ItemInfo
.Rect
.left
;
1450 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1451 if (HelpPos
+ 1 <= i
&&
1452 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1454 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1460 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1463 /***********************************************************************
1464 * DrawMenuBarTemp (USER32.@)
1468 * called by W98SE desk.cpl Control Panel Applet
1470 * Not 100% sure about the param names, but close.
1475 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1477 ROSMENUINFO MenuInfo
;
1478 ROSMENUITEMINFO ItemInfo
;
1480 HFONT FontOld
= NULL
;
1484 Menu
= GetMenu(Wnd
);
1492 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1494 return GetSystemMetrics(SM_CYMENU
);
1497 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1499 FontOld
= SelectObject(DC
, Font
);
1501 if (0 == MenuInfo
.Height
)
1503 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1506 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1508 FillRect(DC
, Rect
, GetSysColorBrush(COLOR_MENU
));
1510 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1511 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1512 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1514 if (0 == MenuInfo
.MenuItemCount
)
1516 SelectObject(DC
, FontOld
);
1517 return GetSystemMetrics(SM_CYMENU
);
1520 MenuInitRosMenuItemInfo(&ItemInfo
);
1521 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1523 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1525 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1526 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1529 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1531 SelectObject(DC
, FontOld
);
1533 return MenuInfo
.Height
;
1537 /***********************************************************************
1540 * Paint a menu bar. Returns the height of the menu bar.
1541 * called from [windows/nonclient.c]
1543 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1545 ROSMENUINFO MenuInfo
;
1546 HFONT FontOld
= NULL
;
1547 HMENU Menu
= GetMenu(Wnd
);
1549 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1551 return GetSystemMetrics(SM_CYMENU
);
1556 FontOld
= SelectObject(DC
, hMenuFont
);
1558 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1560 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1562 if (NULL
!= FontOld
)
1564 SelectObject(DC
, FontOld
);
1566 return MenuInfo
.Height
;
1570 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1574 /***********************************************************************
1577 static BOOL FASTCALL
1578 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1580 DPRINT("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1584 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1585 if (0 == (Flags
& TPM_NONOTIFY
))
1587 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1590 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1592 if (0 == (Flags
& TPM_NONOTIFY
))
1594 ROSMENUINFO MenuInfo
;
1596 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1598 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 == MenuInfo
.Height
)
1600 /* app changed/recreated menu bar entries in WM_INITMENU
1601 Recalculate menu sizes else clicks will not work */
1602 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1603 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1612 /***********************************************************************
1615 * Display a popup menu.
1617 static BOOL FASTCALL
1618 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1619 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1621 ROSMENUINFO MenuInfo
;
1622 ROSMENUITEMINFO ItemInfo
;
1625 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1626 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1628 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1633 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1635 MenuInitRosMenuItemInfo(&ItemInfo
);
1636 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1638 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1639 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1641 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1642 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1645 /* store the owner for DrawItem */
1646 MenuInfo
.WndOwner
= WndOwner
;
1647 MenuSetRosMenuInfo(&MenuInfo
);
1649 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1651 /* adjust popup menu pos so that it fits within the desktop */
1653 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1654 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1656 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1660 X
-= Width
- XAnchor
;
1662 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1664 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1672 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1676 Y
-= Height
+ YAnchor
;
1678 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1680 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1689 /* NOTE: In Windows, top menu popup is not owned. */
1690 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1691 WS_POPUP
, X
, Y
, Width
, Height
,
1692 WndOwner
, 0, (HINSTANCE
) GetWindowLongW(WndOwner
, GWL_HINSTANCE
),
1693 (LPVOID
) MenuInfo
.Self
);
1694 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1698 if (NULL
== TopPopup
)
1700 TopPopup
= MenuInfo
.Wnd
;
1703 /* Display the window */
1704 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1705 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1706 UpdateWindow(MenuInfo
.Wnd
);
1711 /***********************************************************************
1714 * Find a Sub menu. Return the position of the submenu, and modifies
1715 * *hmenu in case it is found in another sub-menu.
1716 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1718 static UINT FASTCALL
1719 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1721 ROSMENUINFO MenuInfo
;
1722 ROSMENUITEMINFO ItemInfo
;
1727 if ((HMENU
) 0xffff == *Menu
1728 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1730 return NO_SELECTED_ITEM
;
1733 MenuInitRosMenuItemInfo(&ItemInfo
);
1734 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1736 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1738 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1739 return NO_SELECTED_ITEM
;
1741 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1745 if (ItemInfo
.hSubMenu
== SubTarget
)
1747 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1750 SubMenu
= ItemInfo
.hSubMenu
;
1751 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1752 if (NO_SELECTED_ITEM
!= Pos
)
1758 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1760 return NO_SELECTED_ITEM
;
1763 /***********************************************************************
1766 static void FASTCALL
1767 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1768 BOOL SendMenuSelect
, HMENU TopMenu
)
1771 ROSMENUITEMINFO ItemInfo
;
1772 ROSMENUINFO TopMenuInfo
;
1775 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1777 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1782 if (MenuInfo
->FocusedItem
== Index
)
1787 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1789 Dc
= GetDC(MenuInfo
->Wnd
);
1793 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1796 if (NULL
== TopPopup
)
1798 TopPopup
= MenuInfo
->Wnd
;
1801 SelectObject(Dc
, hMenuFont
);
1802 MenuInitRosMenuItemInfo(&ItemInfo
);
1804 /* Clear previous highlighted item */
1805 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1807 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1809 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1810 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1812 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1813 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1817 /* Highlight new item (if any) */
1818 MenuInfo
->FocusedItem
= Index
;
1819 MenuSetRosMenuInfo(MenuInfo
);
1820 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1822 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1824 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1826 ItemInfo
.fState
|= MF_HILITE
;
1827 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1828 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
1829 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1834 SendMessageW(WndOwner
, WM_MENUSELECT
,
1835 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
1836 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1837 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
1841 else if (SendMenuSelect
)
1843 if (NULL
!= TopMenu
)
1845 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
1846 if (NO_SELECTED_ITEM
!= Pos
)
1848 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
1849 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
1851 SendMessageW(WndOwner
, WM_MENUSELECT
,
1852 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1854 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1861 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1862 ReleaseDC(MenuInfo
->Wnd
, Dc
);
1865 /***********************************************************************
1868 * Moves currently selected item according to the Offset parameter.
1869 * If there is no selection then it should select the last item if
1870 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1872 static void FASTCALL
1873 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1876 ROSMENUITEMINFO ItemInfo
;
1879 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1881 /* Prevent looping */
1882 if (0 == MenuInfo
->MenuItemCount
|| 0 == Offset
)
1884 else if (Offset
< -1)
1886 else if (Offset
> 1)
1889 MenuInitRosMenuItemInfo(&ItemInfo
);
1891 OrigPos
= MenuInfo
->FocusedItem
;
1892 if (OrigPos
== NO_SELECTED_ITEM
) /* NO_SELECTED_ITEM is not -1 ! */
1899 i
= MenuInfo
->FocusedItem
;
1906 /* Clip and wrap around */
1909 i
= MenuInfo
->MenuItemCount
- 1;
1911 else if (i
>= MenuInfo
->MenuItemCount
)
1915 /* If this is a good candidate; */
1916 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1917 0 == (ItemInfo
.fType
& MF_SEPARATOR
) &&
1918 0 == (ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
1920 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1921 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1924 } while (i
!= OrigPos
);
1927 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1930 /***********************************************************************
1931 * MenuInitSysMenuPopup
1933 * Grey the appropriate items in System menu.
1936 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
1944 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1945 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1946 Gray
= 0 != (Style
& WS_MAXIMIZE
);
1947 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1948 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
1949 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1950 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
1951 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1952 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1953 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1954 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
1956 /* The menu item must keep its state if it's disabled */
1959 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
1962 /* Set default menu item */
1963 if(Style
& WS_MINIMIZE
)
1965 DefItem
= SC_RESTORE
;
1969 if(HitTest
== HTCAPTION
)
1971 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
1979 mii
.cbSize
= sizeof(MENUITEMINFOW
);
1980 mii
.fMask
= MIIM_STATE
;
1981 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
1982 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
1987 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
1990 /***********************************************************************
1993 * Display the sub-menu of the selected item of this menu.
1994 * Return the handle of the submenu, or menu if no submenu to display.
1996 static HMENU FASTCALL
1997 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
1999 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2001 ROSMENUITEMINFO ItemInfo
;
2002 ROSMENUINFO SubMenuInfo
;
2006 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2008 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2010 return MenuInfo
->Self
;
2013 MenuInitRosMenuItemInfo(&ItemInfo
);
2014 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2016 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2017 return MenuInfo
->Self
;
2019 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2021 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2022 return MenuInfo
->Self
;
2025 /* message must be sent before using item,
2026 because nearly everything may be changed by the application ! */
2028 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2029 if (0 == (Flags
& TPM_NONOTIFY
))
2031 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2032 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2035 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2037 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2038 return MenuInfo
->Self
;
2040 Rect
= ItemInfo
.Rect
;
2042 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2043 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2045 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2047 Dc
= GetDC(MenuInfo
->Wnd
);
2051 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2054 SelectObject(Dc
, hMenuFont
);
2056 ItemInfo
.fState
|= MF_HILITE
;
2057 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2058 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2059 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2060 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2063 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2064 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2066 ItemInfo
.Rect
= Rect
;
2069 ItemInfo
.fState
|= MF_MOUSESELECT
;
2071 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2073 if (IS_SYSTEM_MENU(MenuInfo
))
2075 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2076 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2078 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2079 Rect
.top
= Rect
.bottom
;
2080 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2081 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2085 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2086 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2088 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2089 Rect
.top
+= ItemInfo
.Rect
.top
;
2090 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2091 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
;
2095 Rect
.left
+= ItemInfo
.Rect
.left
;
2096 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2097 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2098 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2102 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2103 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2104 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2106 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2109 Ret
= ItemInfo
.hSubMenu
;
2110 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2115 /***********************************************************************
2118 * Hide the sub-popup menus of this menu.
2120 static void FASTCALL
2121 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2123 ROSMENUINFO SubMenuInfo
;
2124 ROSMENUITEMINFO ItemInfo
;
2126 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2128 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2130 MenuInitRosMenuItemInfo(&ItemInfo
);
2131 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2132 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2133 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2135 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2138 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2139 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2140 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2142 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2143 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2144 DestroyWindow(SubMenuInfo
.Wnd
);
2145 SubMenuInfo
.Wnd
= NULL
;
2146 MenuSetRosMenuInfo(&SubMenuInfo
);
2151 /***********************************************************************
2152 * MenuSwitchTracking
2154 * Helper function for menu navigation routines.
2156 static void FASTCALL
2157 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2159 ROSMENUINFO TopMenuInfo
;
2161 DPRINT("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2163 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2164 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2165 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2167 /* both are top level menus (system and menu-bar) */
2168 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2169 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2170 Mt
->TopMenu
= PtMenuInfo
->Self
;
2174 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2177 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2180 /***********************************************************************
2181 * MenuExecFocusedItem
2183 * Execute a menu item (for instance when user pressed Enter).
2184 * Return the wID of the executed item. Otherwise, -1 indicating
2185 * that no menu item was executed;
2186 * Have to receive the flags for the TrackPopupMenu options to avoid
2187 * sending unwanted message.
2191 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2193 ROSMENUITEMINFO ItemInfo
;
2196 DPRINT("%p menu=%p\n", Mt
, MenuInfo
);
2198 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2203 MenuInitRosMenuItemInfo(&ItemInfo
);
2204 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2206 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2210 DPRINT("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2212 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2214 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2215 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2217 /* If TPM_RETURNCMD is set you return the id, but
2218 do not send a message to the owner */
2219 if (0 == (Flags
& TPM_RETURNCMD
))
2221 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2223 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2224 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2228 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2232 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2238 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2244 /***********************************************************************
2247 * Return TRUE if we can go on with menu tracking.
2249 static BOOL FASTCALL
2250 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2253 ROSMENUINFO MenuInfo
;
2254 ROSMENUITEMINFO Item
;
2256 DPRINT("%x PtMenu=%p\n", Mt
, PtMenu
);
2260 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2264 if (IS_SYSTEM_MENU(&MenuInfo
))
2270 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2272 MenuInitRosMenuItemInfo(&Item
);
2273 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2275 MenuCleanupRosMenuItemInfo(&Item
);
2279 if (!(Item
.fType
& MF_SEPARATOR
) &&
2280 !(Item
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2282 if (MenuInfo
.FocusedItem
!= Index
)
2284 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2287 /* If the popup menu is not already "popped" */
2288 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2290 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2294 MenuCleanupRosMenuItemInfo(&Item
);
2299 /* else the click was on the menu bar, finish the tracking */
2304 /***********************************************************************
2307 * Return the value of MenuExecFocusedItem if
2308 * the selected item was not a popup. Else open the popup.
2309 * A -1 return value indicates that we go on with menu tracking.
2313 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2316 ROSMENUINFO MenuInfo
;
2317 ROSMENUITEMINFO ItemInfo
;
2319 DPRINT("%p hmenu=%x\n", Mt
, PtMenu
);
2324 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2329 if (! IS_SYSTEM_MENU(&MenuInfo
))
2331 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2333 MenuInitRosMenuItemInfo(&ItemInfo
);
2334 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2335 MenuInfo
.FocusedItem
== Id
)
2337 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2339 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2340 return MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2342 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2344 /* If we are dealing with the top-level menu */
2345 /* and this is a click on an already "popped" item: */
2346 /* Stop the menu tracking and close the opened submenus */
2347 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2349 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2353 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2354 MenuInfo
.TimeToHide
= TRUE
;
2355 MenuSetRosMenuInfo(&MenuInfo
);
2361 /***********************************************************************
2364 * Walks menu chain trying to find a menu pt maps to.
2366 static HMENU FASTCALL
2367 MenuPtMenu(HMENU Menu
, POINT Pt
)
2369 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2370 ROSMENUINFO MenuInfo
;
2371 ROSMENUITEMINFO ItemInfo
;
2375 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2380 /* try subpopup first (if any) */
2381 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2383 MenuInitRosMenuItemInfo(&ItemInfo
);
2384 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2385 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2386 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2388 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2391 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2395 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2398 /* check the current window (avoiding WM_HITTEST) */
2399 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2400 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2402 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2407 else if (HTSYSMENU
== Ht
)
2409 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2411 else if (HTMENU
== Ht
)
2413 Ret
= GetMenu(MenuInfo
.Wnd
);
2419 /***********************************************************************
2422 * Return TRUE if we can go on with menu tracking.
2424 static BOOL FASTCALL
2425 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2428 ROSMENUINFO MenuInfo
;
2429 ROSMENUITEMINFO ItemInfo
;
2433 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2437 if (IS_SYSTEM_MENU(&MenuInfo
))
2443 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2448 Index
= NO_SELECTED_ITEM
;
2451 if (NO_SELECTED_ITEM
== Index
)
2453 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2454 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2456 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2460 else if (MenuInfo
.FocusedItem
!= Index
)
2462 MenuInitRosMenuItemInfo(&ItemInfo
);
2463 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, Index
, &ItemInfo
) &&
2464 !(ItemInfo
.fType
& MF_SEPARATOR
) &&
2465 !(ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2467 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2468 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2470 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2476 /******************************************************************************
2478 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2480 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2483 PROSMENUITEMINFO MenuItems
;
2485 i
= MenuInfo
->FocusedItem
;
2486 if (NO_SELECTED_ITEM
== i
)
2491 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2493 return NO_SELECTED_ITEM
;
2496 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2498 if (0 != (MenuItems
[i
].fType
& MF_MENUBARBREAK
))
2504 return NO_SELECTED_ITEM
;
2507 /******************************************************************************
2509 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2511 static UINT FASTCALL
2512 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2515 PROSMENUITEMINFO MenuItems
;
2517 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2519 return NO_SELECTED_ITEM
;
2522 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2524 return NO_SELECTED_ITEM
;
2527 /* Find the start of the column */
2529 for (i
= MenuInfo
->FocusedItem
;
2530 0 != i
&& 0 == (MenuItems
[i
].fType
& MF_MENUBARBREAK
);
2538 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2539 return NO_SELECTED_ITEM
;
2542 for (--i
; 0 != i
; --i
)
2544 if (MenuItems
[i
].fType
& MF_MENUBARBREAK
)
2550 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2551 DPRINT("ret %d.\n", i
);
2556 /***********************************************************************
2559 * Return the handle of the selected sub-popup menu (if any).
2561 static HMENU FASTCALL
2562 MenuGetSubPopup(HMENU Menu
)
2564 ROSMENUINFO MenuInfo
;
2565 ROSMENUITEMINFO ItemInfo
;
2567 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2568 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2573 MenuInitRosMenuItemInfo(&ItemInfo
);
2574 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2576 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2579 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2581 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2582 return ItemInfo
.hSubMenu
;
2585 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2589 /***********************************************************************
2592 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2594 static LRESULT FASTCALL
2595 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2597 ROSMENUINFO TopMenuInfo
;
2598 ROSMENUINFO MenuInfo
;
2600 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2602 return (LRESULT
) FALSE
;
2605 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2606 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2608 MDINEXTMENU NextMenu
;
2613 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2614 NextMenu
.hmenuNext
= NULL
;
2615 NextMenu
.hwndNext
= NULL
;
2616 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2618 DPRINT("%p [%p] -> %p [%p]\n",
2619 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2621 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2623 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2624 NewWnd
= Mt
->OwnerWnd
;
2625 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2627 /* switch to the menu bar */
2629 if (0 != (Style
& WS_CHILD
)
2630 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2637 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2641 Id
= MenuInfo
.MenuItemCount
- 1;
2644 else if (0 != (Style
& WS_SYSMENU
))
2646 /* switch to the system menu */
2647 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2654 else /* application returned a new menu to switch to */
2656 NewMenu
= NextMenu
.hmenuNext
;
2657 NewWnd
= NextMenu
.hwndNext
;
2659 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2661 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2663 if (0 != (Style
& WS_SYSMENU
)
2664 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2666 /* get the real system menu */
2667 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2669 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2671 /* FIXME: Not sure what to do here;
2672 * perhaps try to track NewMenu as a popup? */
2674 DPRINT(" -- got confused.\n");
2684 if (NewMenu
!= Mt
->TopMenu
)
2686 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2688 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2690 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2694 if (NewWnd
!= Mt
->OwnerWnd
)
2696 Mt
->OwnerWnd
= NewWnd
;
2697 SetCapture(Mt
->OwnerWnd
);
2698 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2701 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2702 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2704 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2713 /***********************************************************************
2716 * The idea is not to show the popup if the next input message is
2717 * going to hide it anyway.
2719 static BOOL FASTCALL
2720 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2724 Msg
.hwnd
= Mt
->OwnerWnd
;
2726 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2727 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2732 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2733 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2735 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2736 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2737 if (WM_KEYDOWN
== Msg
.message
2738 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2740 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2747 /* failures go through this */
2748 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2753 /***********************************************************************
2756 * Handle a VK_ESCAPE key event in a menu.
2758 static BOOL FASTCALL
2759 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2761 BOOL EndMenu
= TRUE
;
2762 ROSMENUINFO MenuInfo
;
2763 HMENU MenuTmp
, MenuPrev
;
2765 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2767 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2768 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2770 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2772 /* close topmost popup */
2773 while (MenuTmp
!= Mt
->CurrentMenu
)
2776 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2779 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2781 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2783 Mt
->CurrentMenu
= MenuPrev
;
2791 /***********************************************************************
2794 * Handle a VK_LEFT key event in a menu.
2796 static void FASTCALL
2797 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2799 ROSMENUINFO MenuInfo
;
2800 ROSMENUINFO TopMenuInfo
;
2801 ROSMENUINFO PrevMenuInfo
;
2802 HMENU MenuTmp
, MenuPrev
;
2805 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2807 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2812 /* Try to move 1 column left (if possible) */
2813 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2815 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2817 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2822 /* close topmost popup */
2823 while (MenuTmp
!= Mt
->CurrentMenu
)
2826 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2829 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
2833 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
2834 Mt
->CurrentMenu
= MenuPrev
;
2836 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2840 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
2842 /* move menu bar selection if no more popups are left */
2844 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
2846 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
2849 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2851 /* A sublevel menu was displayed - display the next one
2852 * unless there is another displacement coming up */
2854 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2855 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2857 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
2864 /***********************************************************************
2867 * Handle a VK_RIGHT key event in a menu.
2869 static void FASTCALL
2870 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
2873 ROSMENUINFO MenuInfo
;
2874 ROSMENUINFO CurrentMenuInfo
;
2877 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2878 Mt
->CurrentMenu
, Mt
->TopMenu
);
2880 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2884 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
2886 /* If already displaying a popup, try to display sub-popup */
2888 MenuTmp
= Mt
->CurrentMenu
;
2889 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2891 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
2894 /* if subpopup was displayed then we are done */
2895 if (MenuTmp
!= Mt
->CurrentMenu
)
2901 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2906 /* Check to see if there's another column */
2907 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
2909 DPRINT("Going to %d.\n", NextCol
);
2910 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2912 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
2917 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
2919 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2921 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
2922 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
2929 /* try to move to the next item */
2930 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
2932 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
2935 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2937 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2938 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2940 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
2947 /***********************************************************************
2950 * Find the menu item selected by a key press.
2951 * Return item id, -1 if none, -2 if we should close the menu.
2953 static UINT FASTCALL
2954 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
2955 WCHAR Key
, BOOL ForceMenuChar
)
2957 ROSMENUINFO SysMenuInfo
;
2958 PROSMENUITEMINFO Items
, ItemInfo
;
2962 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
2964 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
2966 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
2968 MenuInfo
= &SysMenuInfo
;
2976 if (NULL
!= MenuInfo
)
2978 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
2982 if (! ForceMenuChar
)
2984 Key
= towupper(Key
);
2986 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
2988 if (IS_STRING_ITEM(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
2990 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
2993 p
= wcschr(p
+ 2, '&');
2995 while (NULL
!= p
&& L
'&' == p
[1]);
2996 if (NULL
!= p
&& (towupper(p
[1]) == Key
))
3004 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3005 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3006 if (2 == HIWORD(MenuChar
))
3008 return LOWORD(MenuChar
);
3010 if (1 == HIWORD(MenuChar
))
3019 /***********************************************************************
3022 * Menu tracking code.
3025 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3026 HWND Wnd
, const RECT
*Rect
)
3029 ROSMENUINFO MenuInfo
;
3030 ROSMENUITEMINFO ItemInfo
;
3032 INT ExecutedMenuId
= -1;
3034 BOOL EnterIdleSent
= FALSE
;
3037 Mt
.CurrentMenu
= Menu
;
3043 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3044 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3045 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3048 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3053 if (0 != (Flags
& TPM_BUTTONDOWN
))
3055 /* Get the result in order to start the tracking or not */
3056 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3057 fEndMenu
= ! fRemove
;
3060 SetCapture(Mt
.OwnerWnd
);
3061 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3065 /* we have to keep the message in the queue until it's
3066 * clear that menu loop is not over yet. */
3070 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3072 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3076 /* remove the message from the queue */
3077 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3081 if (! EnterIdleSent
)
3083 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3084 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3085 EnterIdleSent
= TRUE
;
3086 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3092 /* check if EndMenu() tried to cancel us, by posting this message */
3093 if (WM_CANCELMODE
== Msg
.message
)
3095 /* we are now out of the loop */
3098 /* remove the message from the queue */
3099 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3101 /* break out of internal loop, ala ESCAPE */
3105 TranslateMessage(&Msg
);
3108 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3110 EnterIdleSent
= FALSE
;
3114 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3117 * Use the mouse coordinates in lParam instead of those in the MSG
3118 * struct to properly handle synthetic messages. They are already
3119 * in screen coordinates.
3121 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3122 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3124 /* Find a menu for this mouse event */
3125 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3129 /* no WM_NC... messages in captured state */
3131 case WM_RBUTTONDBLCLK
:
3132 case WM_RBUTTONDOWN
:
3133 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3138 case WM_LBUTTONDBLCLK
:
3139 case WM_LBUTTONDOWN
:
3140 /* If the message belongs to the menu, removes it from the queue */
3141 /* Else, end menu tracking */
3142 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3143 fEndMenu
= ! fRemove
;
3147 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3153 /* Check if a menu was selected by the mouse */
3156 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3158 /* End the loop if ExecutedMenuId is an item ID */
3159 /* or if the job was done (ExecutedMenuId = 0). */
3160 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3164 /* No menu was selected by the mouse */
3165 /* if the function was called by TrackPopupMenu, continue
3166 with the menu tracking. If not, stop it */
3167 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3174 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3178 } /* switch(Msg.message) - mouse */
3180 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3182 fRemove
= TRUE
; /* Keyboard messages are always removed */
3190 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3192 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3198 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3200 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3201 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3205 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3206 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3208 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3210 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3212 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3216 else /* otherwise try to move selection */
3218 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3224 MenuKeyLeft(&Mt
, Flags
);
3228 MenuKeyRight(&Mt
, Flags
);
3232 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3238 hi
.cbSize
= sizeof(HELPINFO
);
3239 hi
.iContextType
= HELPINFO_MENUITEM
;
3240 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3242 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3248 MenuInitRosMenuItemInfo(&ItemInfo
);
3249 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3250 MenuInfo
.FocusedItem
,
3253 hi
.iCtrlId
= ItemInfo
.wID
;
3259 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3262 hi
.hItemHandle
= Menu
;
3263 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3264 hi
.MousePos
= Msg
.pt
;
3265 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3272 break; /* WM_KEYDOWN */
3281 break; /* WM_SYSKEYDOWN */
3287 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3291 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3293 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3294 fEndMenu
= (ExecutedMenuId
!= -1);
3298 /* Hack to avoid control chars. */
3299 /* We will find a better way real soon... */
3300 if (Msg
.wParam
< 32)
3305 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3306 LOWORD(Msg
.wParam
), FALSE
);
3307 if ((UINT
) -2 == Pos
)
3311 else if ((UINT
) -1 == Pos
)
3317 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3318 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3319 fEndMenu
= (-1 != ExecutedMenuId
);
3323 } /* switch(msg.message) - kbd */
3327 DispatchMessageW(&Msg
);
3335 /* finally remove message from the queue */
3337 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3339 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3343 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3347 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3348 SetCapture(NULL
); /* release the capture */
3350 /* If dropdown is still painted and the close box is clicked on
3351 then the menu will be destroyed as part of the DispatchMessage above.
3352 This will then invalidate the menu handle in Mt.hTopMenu. We should
3353 check for this first. */
3354 if (IsMenu(Mt
.TopMenu
))
3356 if (IsWindow(Mt
.OwnerWnd
))
3358 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3360 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3362 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3364 DestroyWindow(MenuInfo
.Wnd
);
3365 MenuInfo
.Wnd
= NULL
;
3367 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3370 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3373 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3375 /* Reset the variable for hiding menu */
3376 MenuInfo
.TimeToHide
= FALSE
;
3377 MenuSetRosMenuInfo(&MenuInfo
);
3381 /* The return value is only used by TrackPopupMenu */
3382 return (-1 != ExecutedMenuId
) ? ExecutedMenuId
: 0;
3385 /***********************************************************************
3388 static BOOL FASTCALL
3389 MenuExitTracking(HWND Wnd
)
3391 DPRINT("hwnd=%p\n", Wnd
);
3393 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3400 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3402 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3403 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3405 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3409 /* map point to parent client coordinates */
3410 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3411 if (Parent
!= GetDesktopWindow())
3413 ScreenToClient(Parent
, &Pt
);
3416 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3417 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3418 MenuExitTracking(Wnd
);
3424 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
3428 /* FUNCTIONS *****************************************************************/
3431 MenuIsStringItem(ULONG TypeData)
3433 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3441 AppendMenuA(HMENU hMenu
,
3443 UINT_PTR uIDNewItem
,
3446 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3455 AppendMenuW(HMENU hMenu
,
3457 UINT_PTR uIDNewItem
,
3460 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3469 CheckMenuItem(HMENU hmenu
,
3473 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3481 CheckMenuRadioItem(HMENU hmenu
,
3488 PROSMENUITEMINFO Items
;
3492 mi
.cbSize
= sizeof(MENUINFO
);
3496 if(idFirst
> idLast
) return ret
;
3498 if(!NtUserMenuInfo(hmenu
, &mi
, FALSE
)) return ret
;
3500 if(MenuGetAllRosMenuItemInfo(mi
.Self
, &Items
) <= 0) return ret
;
3502 for (i
= 0 ; i
< mi
.MenuItemCount
; i
++)
3504 if (0 != (Items
[i
].fType
& MF_MENUBARBREAK
)) break;
3505 if ( i
>= idFirst
&& i
<= idLast
)
3509 Items
[i
].fType
|= MFT_RADIOCHECK
;
3510 Items
[i
].fState
|= MFS_CHECKED
;
3514 Items
[i
].fType
&= ~MFT_RADIOCHECK
;
3515 Items
[i
].fState
&= ~MFS_CHECKED
;
3517 if(!MenuSetRosMenuItemInfo(mi
.Self
, i
,&Items
[i
]))
3520 if ( i
== mi
.MenuItemCount
) ret
= TRUE
;
3522 MenuCleanupRosMenuItemInfo(Items
);
3534 return NtUserCreateMenu(FALSE
);
3542 CreatePopupMenu(VOID
)
3545 return NtUserCreateMenu(TRUE
);
3553 DeleteMenu(HMENU hMenu
,
3557 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
3565 DestroyMenu(HMENU hMenu
)
3567 return NtUserDestroyMenu(hMenu
);
3575 DrawMenuBar(HWND hWnd
)
3577 return (BOOL
)NtUserCallHwndLock(hWnd
, HWNDLOCK_ROUTINE_DRAWMENUBAR
);
3585 EnableMenuItem(HMENU hMenu
,
3589 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
3599 guii
.cbSize
= sizeof(GUITHREADINFO
);
3600 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
3602 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
3614 return NtUserGetMenu(hWnd
);
3622 GetMenuBarInfo(HWND hwnd
,
3627 return (BOOL
)NtUserGetMenuBarInfo(hwnd
, idObject
, idItem
, pmbi
);
3635 GetMenuCheckMarkDimensions(VOID
)
3637 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
3638 GetSystemMetrics(SM_CYMENUCHECK
)));
3646 GetMenuDefaultItem(HMENU hMenu
,
3650 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
3658 GetMenuInfo(HMENU hmenu
,
3664 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
3667 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
3668 mi
.cbSize
= sizeof(MENUINFO
);
3669 mi
.fMask
= lpcmi
->fMask
;
3671 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
3673 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
3682 GetMenuItemCount(HMENU Menu
)
3684 ROSMENUINFO MenuInfo
;
3686 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
3694 GetMenuItemID(HMENU hMenu
,
3697 ROSMENUITEMINFO mii
;
3699 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3700 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
3702 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
3707 if (NULL
!= mii
.hSubMenu
)
3728 LPMENUITEMINFOA mii
)
3733 if (mii
->cbSize
!= sizeof(MENUITEMINFOA
) &&
3734 mii
->cbSize
!= sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
))
3736 SetLastError(ERROR_INVALID_PARAMETER
);
3740 if ((mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)) == 0)
3742 /* No text requested, just pass on */
3743 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3746 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
3747 AnsiBuffer
= mii
->dwTypeData
;
3749 if (AnsiBuffer
!= NULL
)
3751 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
3752 miiW
.cch
* sizeof(WCHAR
));
3753 if (miiW
.dwTypeData
== NULL
)
3757 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)&miiW
, FALSE
))
3759 HeapFree(GetProcessHeap(), 0, miiW
.dwTypeData
);
3763 if (AnsiBuffer
!= NULL
)
3765 if (IS_STRING_ITEM(miiW
.fType
))
3767 WideCharToMultiByte(CP_ACP
, 0, miiW
.dwTypeData
, miiW
.cch
, AnsiBuffer
,
3768 mii
->cch
, NULL
, NULL
);
3770 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
3773 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
);
3774 mii
->dwTypeData
= AnsiBuffer
;
3775 mii
->cch
= strlen(AnsiBuffer
);
3788 LPMENUITEMINFOW mii
)
3790 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3798 GetMenuItemRect(HWND hWnd
,
3803 return NtUserGetMenuItemRect( hWnd
, hMenu
, uItem
, lprcItem
);
3817 ROSMENUINFO MenuInfo
;
3818 ROSMENUITEMINFO mii
;
3820 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3821 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
3822 mii
.dwTypeData
= NULL
;
3825 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
3830 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
3834 nSubItems
= MenuInfo
.MenuItemCount
;
3836 /* FIXME - ported from wine, does that work (0xff)? */
3837 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
3838 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
3840 return (UINT
)-1; /* Invalid submenu */
3843 /* FIXME - ported from wine, does that work? */
3844 return (mii
.fType
| mii
.fState
);
3864 mii
.dwTypeData
= lpString
;
3865 mii
.fMask
= MIIM_STRING
;
3866 mii
.fType
= MF_STRING
;
3867 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3868 mii
.cch
= nMaxCount
;
3870 if(!(GetMenuItemInfoA( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&mii
)))
3890 miiW
.dwTypeData
= lpString
;
3891 miiW
.fMask
= MIIM_STRING
;
3892 miiW
.cbSize
= sizeof(MENUITEMINFOW
);
3893 miiW
.cch
= nMaxCount
;
3895 if(!(GetMenuItemInfoW( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&miiW
)))
3913 mi
.cbSize
= sizeof(MENUITEMINFOW
);
3914 mi
.fMask
= MIIM_SUBMENU
;
3916 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
3918 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
3935 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
3937 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
3952 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
3965 UINT_PTR uIDNewItem
,
3969 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3970 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
3972 mii
.fState
= MFS_ENABLED
;
3974 if(uFlags
& MF_BITMAP
)
3976 mii
.fType
|= MFT_BITMAP
;
3977 mii
.fMask
|= MIIM_BITMAP
;
3978 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
3980 else if(uFlags
& MF_OWNERDRAW
)
3982 mii
.fType
|= MFT_OWNERDRAW
;
3983 mii
.fMask
|= MIIM_DATA
;
3984 mii
.dwItemData
= (DWORD
) lpNewItem
;
3988 mii
.fMask
|= MIIM_TYPE
;
3989 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
3990 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
3993 if(uFlags
& MF_RIGHTJUSTIFY
)
3995 mii
.fType
|= MFT_RIGHTJUSTIFY
;
3997 if(uFlags
& MF_MENUBREAK
)
3999 mii
.fType
|= MFT_MENUBREAK
;
4001 if(uFlags
& MF_MENUBARBREAK
)
4003 mii
.fType
|= MFT_MENUBARBREAK
;
4005 if(uFlags
& MF_DISABLED
)
4007 mii
.fState
|= MFS_DISABLED
;
4009 if(uFlags
& MF_GRAYED
)
4011 mii
.fState
|= MFS_GRAYED
;
4014 if(uFlags
& MF_POPUP
)
4016 mii
.fType
|= MF_POPUP
;
4017 mii
.fMask
|= MIIM_SUBMENU
;
4018 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4022 mii
.fMask
|= MIIM_ID
;
4023 mii
.wID
= (UINT
)uIDNewItem
;
4025 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4038 LPCMENUITEMINFOA lpmii
)
4041 UNICODE_STRING MenuText
;
4043 BOOL CleanHeap
= FALSE
;
4046 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4047 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4049 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4051 /* copy the text string */
4052 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4053 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4055 Status
= RtlCreateUnicodeStringFromAsciiz(&MenuText
, (LPSTR
)mi
.dwTypeData
);
4056 if (!NT_SUCCESS (Status
))
4058 SetLastError (RtlNtStatusToDosError(Status
));
4061 mi
.dwTypeData
= MenuText
.Buffer
;
4062 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4066 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4068 if ( CleanHeap
) RtlFreeUnicodeString ( &MenuText
);
4083 LPCMENUITEMINFOW lpmii
)
4086 UNICODE_STRING MenuText
;
4088 mi
.hbmpItem
= (HBITMAP
)0;
4090 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4091 if a bad user passes bad data, we crash his process instead of the
4094 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4095 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4097 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
4099 /* copy the text string */
4100 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4101 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) &&
4102 mi
.dwTypeData
!= NULL
)
4104 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4105 mi
.dwTypeData
= MenuText
.Buffer
;
4106 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4109 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4124 UINT_PTR uIDNewItem
,
4128 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4129 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4131 mii
.fState
= MFS_ENABLED
;
4133 if(uFlags
& MF_BITMAP
)
4135 mii
.fType
|= MFT_BITMAP
;
4136 mii
.fMask
|= MIIM_BITMAP
;
4137 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4139 else if(uFlags
& MF_OWNERDRAW
)
4141 mii
.fType
|= MFT_OWNERDRAW
;
4142 mii
.fMask
|= MIIM_DATA
;
4143 mii
.dwItemData
= (DWORD
) lpNewItem
;
4147 mii
.fMask
|= MIIM_TYPE
;
4148 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4149 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4152 if(uFlags
& MF_RIGHTJUSTIFY
)
4154 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4156 if(uFlags
& MF_MENUBREAK
)
4158 mii
.fType
|= MFT_MENUBREAK
;
4160 if(uFlags
& MF_MENUBARBREAK
)
4162 mii
.fType
|= MFT_MENUBARBREAK
;
4164 if(uFlags
& MF_DISABLED
)
4166 mii
.fState
|= MFS_DISABLED
;
4168 if(uFlags
& MF_GRAYED
)
4170 mii
.fState
|= MFS_GRAYED
;
4173 if(uFlags
& MF_POPUP
)
4175 mii
.fType
|= MF_POPUP
;
4176 mii
.fMask
|= MIIM_SUBMENU
;
4177 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4181 mii
.fMask
|= MIIM_ID
;
4182 mii
.wID
= (UINT
)uIDNewItem
;
4184 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4196 ROSMENUINFO MenuInfo
;
4198 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4206 LoadMenuA(HINSTANCE hInstance
,
4209 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4210 if (Resource
== NULL
)
4214 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4222 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4224 return(LoadMenuIndirectW(lpMenuTemplate
));
4232 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4235 WORD version
, offset
;
4236 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4238 version
= GET_WORD(p
);
4243 case 0: /* standard format is version of 0 */
4244 offset
= GET_WORD(p
);
4245 p
+= sizeof(WORD
) + offset
;
4246 if (!(hMenu
= CreateMenu())) return 0;
4247 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4253 case 1: /* extended format is version of 1 */
4254 offset
= GET_WORD(p
);
4255 p
+= sizeof(WORD
) + offset
;
4256 if (!(hMenu
= CreateMenu())) return 0;
4257 if (!MENUEX_ParseResource(p
, hMenu
))
4259 DestroyMenu( hMenu
);
4264 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4274 LoadMenuW(HINSTANCE hInstance
,
4277 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4278 if (Resource
== NULL
)
4282 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4296 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4309 UINT_PTR uIDNewItem
,
4313 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4314 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4316 mii
.fState
= MFS_ENABLED
;
4320 if(!GetMenuItemInfoA( hMnu
,
4322 (BOOL
)!(MF_BYPOSITION
& uFlags
),
4323 &mii
)) return FALSE
;
4325 if(uFlags
& MF_BITMAP
)
4327 mii
.fType
|= MFT_BITMAP
;
4328 mii
.fMask
|= MIIM_BITMAP
;
4329 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4331 else if(uFlags
& MF_OWNERDRAW
)
4333 mii
.fType
|= MFT_OWNERDRAW
;
4334 mii
.fMask
|= MIIM_DATA
;
4335 mii
.dwItemData
= (DWORD
) lpNewItem
;
4337 else /* Default action MF_STRING. */
4339 if(mii
.dwTypeData
!= NULL
)
4341 HeapFree(GetProcessHeap(),0, mii
.dwTypeData
);
4343 /* Item beginning with a backspace is a help item */
4344 if (*lpNewItem
== '\b')
4346 mii
.fType
|= MF_HELP
;
4349 mii
.fMask
|= MIIM_TYPE
;
4350 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
4351 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
4354 if(uFlags
& MF_RIGHTJUSTIFY
)
4356 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4358 if(uFlags
& MF_MENUBREAK
)
4360 mii
.fType
|= MFT_MENUBREAK
;
4362 if(uFlags
& MF_MENUBARBREAK
)
4364 mii
.fType
|= MFT_MENUBARBREAK
;
4366 if(uFlags
& MF_DISABLED
)
4368 mii
.fState
|= MFS_DISABLED
;
4370 if(uFlags
& MF_GRAYED
)
4372 mii
.fState
|= MFS_GRAYED
;
4375 if ((mii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (mii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4376 NtUserDestroyMenu( mii
.hSubMenu
); /* ModifyMenu() spec */
4378 if(uFlags
& MF_POPUP
)
4380 mii
.fType
|= MF_POPUP
;
4381 mii
.fMask
|= MIIM_SUBMENU
;
4382 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4386 mii
.fMask
|= MIIM_ID
;
4387 mii
.wID
= (UINT
)uIDNewItem
;
4390 return SetMenuItemInfoA( hMnu
,
4392 (BOOL
)!(MF_BYPOSITION
& uFlags
),
4406 UINT_PTR uIDNewItem
,
4410 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4411 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4413 mii
.fState
= MFS_ENABLED
;
4417 if(!NtUserMenuItemInfo( hMnu
,
4419 (BOOL
)!(MF_BYPOSITION
& uFlags
),
4420 (PROSMENUITEMINFO
) &mii
,
4421 FALSE
)) return FALSE
;
4423 if(uFlags
& MF_BITMAP
)
4425 mii
.fType
|= MFT_BITMAP
;
4426 mii
.fMask
|= MIIM_BITMAP
;
4427 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4429 else if(uFlags
& MF_OWNERDRAW
)
4431 mii
.fType
|= MFT_OWNERDRAW
;
4432 mii
.fMask
|= MIIM_DATA
;
4433 mii
.dwItemData
= (DWORD
) lpNewItem
;
4437 if(mii
.dwTypeData
!= NULL
)
4439 HeapFree(GetProcessHeap(),0, mii
.dwTypeData
);
4441 if (*lpNewItem
== '\b')
4443 mii
.fType
|= MF_HELP
;
4446 mii
.fMask
|= MIIM_TYPE
;
4447 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4448 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4451 if(uFlags
& MF_RIGHTJUSTIFY
)
4453 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4455 if(uFlags
& MF_MENUBREAK
)
4457 mii
.fType
|= MFT_MENUBREAK
;
4459 if(uFlags
& MF_MENUBARBREAK
)
4461 mii
.fType
|= MFT_MENUBARBREAK
;
4463 if(uFlags
& MF_DISABLED
)
4465 mii
.fState
|= MFS_DISABLED
;
4467 if(uFlags
& MF_GRAYED
)
4469 mii
.fState
|= MFS_GRAYED
;
4472 if ((mii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (mii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4473 NtUserDestroyMenu( mii
.hSubMenu
);
4475 if(uFlags
& MF_POPUP
)
4477 mii
.fType
|= MF_POPUP
;
4478 mii
.fMask
|= MIIM_SUBMENU
;
4479 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4483 mii
.fMask
|= MIIM_ID
;
4484 mii
.wID
= (UINT
)uIDNewItem
;
4487 return SetMenuItemInfoW( hMnu
,
4489 (BOOL
)!(MF_BYPOSITION
& uFlags
),
4504 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
4515 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4529 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
4544 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4547 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4548 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4561 HBITMAP hBitmapUnchecked
,
4562 HBITMAP hBitmapChecked
)
4578 LPCMENUITEMINFOA lpmii
)
4580 MENUITEMINFOW MenuItemInfoW
;
4581 UNICODE_STRING UnicodeString
;
4584 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4586 if ((MenuItemInfoW
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4587 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
) &&
4588 MenuItemInfoW
.dwTypeData
!= NULL
)
4590 RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4591 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4592 MenuItemInfoW
.dwTypeData
= UnicodeString
.Buffer
;
4593 MenuItemInfoW
.cch
= UnicodeString
.Length
/ sizeof(WCHAR
);
4597 UnicodeString
.Buffer
= NULL
;
4600 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4601 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4603 if (UnicodeString
.Buffer
!= NULL
)
4605 RtlFreeUnicodeString(&UnicodeString
);
4621 LPCMENUITEMINFOW lpmii
)
4623 MENUITEMINFOW MenuItemInfoW
;
4625 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4626 MenuItemInfoW
.cch
= wcslen(MenuItemInfoW
.dwTypeData
);
4628 return NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4629 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4643 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4648 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4651 return NtUserSetSystemMenu(hwnd
, hMenu
);
4671 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
4673 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4674 if (0 == (Flags
& TPM_NONOTIFY
))
4676 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
4679 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
4681 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
4683 MenuExitTracking(Wnd
);
4702 /* Not fully implemented */
4703 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
4704 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
4713 SetMenuContextHelpId(HMENU hmenu
,
4714 DWORD dwContextHelpId
)
4716 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
4725 GetMenuContextHelpId(HMENU hmenu
)
4728 mi
.cbSize
= sizeof(ROSMENUINFO
);
4729 mi
.fMask
= MIM_HELPID
;
4731 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
4733 return mi
.dwContextHelpID
;
4778 LPCWSTR lpszNewItem
,
4783 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4784 for MF_DELETE. We should check the parameters for all others
4785 MF_* actions also (anybody got a doc on ChangeMenu?).
4788 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4791 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4794 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4797 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4800 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4801 flags
&~ MF_REMOVE
);
4803 default : /* MF_INSERT */
4804 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
4821 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4822 for MF_DELETE. We should check the parameters for all others
4823 MF_* actions also (anybody got a doc on ChangeMenu?).
4826 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4829 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4832 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4835 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4838 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4839 flags
&~ MF_REMOVE
);
4841 default : /* MF_INSERT */
4842 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);