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 ******************************************************************/
40 /* internal popup menu window messages */
41 #define MM_SETMENUHANDLE (WM_USER + 0)
42 #define MM_GETMENUHANDLE (WM_USER + 1)
44 /* Internal MenuTrackMenu() flags */
45 #define TPM_INTERNAL 0xF0000000
46 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
47 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
48 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
50 /* TYPES *********************************************************************/
52 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
54 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
55 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
56 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
58 #define IS_SYSTEM_MENU(MenuInfo) \
59 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
61 #define IS_SYSTEM_POPUP(MenuInfo) \
62 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
64 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
66 #define MENU_BAR_ITEMS_SPACE (12)
67 #define SEPARATOR_HEIGHT (5)
68 #define MENU_TAB_SPACE (8)
74 #define MF_END (0x0080)
78 #define MIIM_STRING (0x00000040)
81 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
82 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
83 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
84 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
86 /* internal flags for menu tracking */
88 #define TF_ENDMENU 0x0001
89 #define TF_SUSPENDPOPUP 0x0002
90 #define TF_SKIPREMOVE 0x0004
95 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
96 HMENU TopMenu
; /* initial menu */
97 HWND OwnerWnd
; /* where notifications are sent */
101 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
103 /*********************************************************************
104 * PopupMenu class descriptor
106 const struct builtin_class_descr POPUPMENU_builtin_class
=
108 POPUPMENU_CLASS_ATOMW
, /* name */
109 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
110 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
111 (WNDPROC
) NULL
, /* FIXME - procA */
112 sizeof(MENUINFO
*), /* extra */
113 (LPCWSTR
) IDC_ARROW
, /* cursor */
114 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
118 /* INTERNAL FUNCTIONS ********************************************************/
120 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
121 * Of course I didnt copy the ASM code because we want this to be portable
122 * and it needs to go away.
126 #define GET_WORD(ptr) (*(WORD *)(ptr))
129 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
132 HFONT hMenuFont
= NULL
;
133 HFONT hMenuFontBold
= NULL
;
135 /* Flag set by EndMenu() to force an exit from menu tracking */
136 static BOOL fEndMenu
= FALSE
;
138 /* Use global popup window because there's no way 2 menus can
139 * be tracked at the same time. */
140 static HWND TopPopup
;
142 /* Dimension of the menu bitmaps */
143 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
145 static HBITMAP StdMnArrow
= NULL
;
146 static HBITMAP BmpSysMenu
= NULL
;
148 /***********************************************************************
151 * Get full information about menu
154 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
156 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
157 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
159 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
162 /***********************************************************************
165 * Set full information about menu
168 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
170 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
171 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
173 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
176 /***********************************************************************
177 * MenuInitRosMenuItemInfo
179 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
182 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
184 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
185 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
188 /***********************************************************************
189 * MenuGetRosMenuItemInfo
191 * Get full information about a menu item
194 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
196 if (ItemInfo
->dwTypeData
!= NULL
)
198 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
201 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
202 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
203 ItemInfo
->dwTypeData
= NULL
;
205 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
211 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
)
214 ItemInfo
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0,
215 ItemInfo
->cch
* sizeof(WCHAR
));
216 if (NULL
== ItemInfo
->dwTypeData
)
221 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
231 /***********************************************************************
232 * MenuSetRosMenuItemInfo
234 * Set full information about a menu item
237 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
241 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
&&
242 ItemInfo
->dwTypeData
!= NULL
)
244 ItemInfo
->cch
= wcslen(ItemInfo
->dwTypeData
);
246 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
247 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
250 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
255 /***********************************************************************
256 * MenuCleanupRosMenuItemInfo
258 * Cleanup after use of MenuGet/SetRosMenuItemInfo
261 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
263 if (ItemInfo
->dwTypeData
!= NULL
)
265 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
269 /***********************************************************************
270 * MenuGetAllRosMenuItemInfo
272 * Get full information about all menu items
275 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
279 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
284 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
285 if (NULL
== *ItemInfo
)
290 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
293 /***********************************************************************
294 * MenuCleanupAllRosMenuItemInfo
296 * Cleanup after use of MenuGetAllRosMenuItemInfo
299 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
301 HeapFree(GetProcessHeap(), 0, ItemInfo
);
305 /***********************************************************************
308 * Load the arrow bitmap. We can't do this from MenuInit since user32
309 * can also be used (and thus initialized) from text-mode.
312 MenuLoadBitmaps(VOID
)
314 /* Load menu bitmaps */
315 if (NULL
== StdMnArrow
)
317 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
319 if (NULL
!= StdMnArrow
)
322 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
323 ArrowBitmapWidth
= bm
.bmWidth
;
324 ArrowBitmapHeight
= bm
.bmHeight
;
328 /* Load system buttons bitmaps */
329 if (NULL
== BmpSysMenu
)
331 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
335 /***********************************************************************
336 * MenuGetBitmapItemSize
338 * Get the size of a bitmap item.
341 MenuGetBitmapItemSize(UINT Id
, DWORD Data
, SIZE
*Size
)
344 HBITMAP Bmp
= (HBITMAP
) Id
;
346 Size
->cx
= Size
->cy
= 0;
348 /* check if there is a magic menu item associated with this item */
349 if (0 != Id
&& IS_MAGIC_ITEM(Id
))
351 switch((INT_PTR
) LOWORD(Id
))
353 case (INT_PTR
) HBMMENU_SYSTEM
:
356 Bmp
= (HBITMAP
) Data
;
360 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
361 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
362 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
363 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
364 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
365 /* FIXME: Why we need to subtract these magic values? */
366 Size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
367 Size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
369 case (INT_PTR
) HBMMENU_CALLBACK
:
370 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
371 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
372 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
373 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
375 DPRINT("Magic menu bitmap not implemented\n");
380 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
382 Size
->cx
= Bm
.bmWidth
;
383 Size
->cy
= Bm
.bmHeight
;
387 /***********************************************************************
390 * Draw a bitmap item.
393 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
, BOOL MenuBar
)
398 HBITMAP Bmp
= (HBITMAP
) Item
->hbmpItem
;
399 int w
= Rect
->right
- Rect
->left
;
400 int h
= Rect
->bottom
- Rect
->top
;
404 /* Check if there is a magic menu item associated with this item */
405 if (IS_MAGIC_ITEM(Item
->hbmpItem
))
411 switch ((int) Item
->hbmpItem
)
413 case (INT_PTR
) HBMMENU_SYSTEM
:
414 if (NULL
!= Item
->hbmpItem
)
416 Bmp
= Item
->hbmpItem
;
417 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
425 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
429 /* only use right half of the bitmap */
430 BmpXoffset
= Bm
.bmWidth
/ 2;
431 Bm
.bmWidth
-= BmpXoffset
;
434 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
435 Flags
= DFCS_CAPTIONRESTORE
;
437 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
439 Flags
= DFCS_CAPTIONMIN
;
441 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
443 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
445 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
446 Flags
= DFCS_CAPTIONCLOSE
;
448 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
449 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
451 case (INT_PTR
) HBMMENU_CALLBACK
:
452 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
453 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
454 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
455 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
457 DPRINT("Magic menu bitmap not implemented\n");
460 InflateRect(&r
, -1, -1);
461 if (0 != (Item
->fState
& MF_HILITE
))
463 Flags
|= DFCS_PUSHED
;
465 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
469 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
475 DcMem
= CreateCompatibleDC(Dc
);
476 SelectObject(DcMem
, Bmp
);
478 /* handle fontsize > bitmap_height */
479 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
481 Rop
= (0 != (Item
->fState
& MF_HILITE
) && ! IS_MAGIC_ITEM(Item
->hbmpItem
)) ? NOTSRCCOPY
: SRCCOPY
;
482 if (0 != (Item
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(Item
->fType
))
484 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
486 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
490 /***********************************************************************
493 * Draw a single menu item.
496 MenuDrawMenuItem(HWND Wnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
497 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
502 if (0 != (Item
->fType
& MF_SYSMENU
))
506 UserGetInsideRectNC(Wnd
, &Rect
);
507 UserDrawSysMenuButton(Wnd
, Dc
, &Rect
,
508 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
516 if (0 != (Item
->fState
& MF_HILITE
))
520 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
521 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
525 if (0 != (Item
->fState
& MF_GRAYED
))
527 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
531 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
533 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
538 if (0 != (Item
->fState
& MF_GRAYED
))
540 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
544 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
546 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
549 if (0 != (Item
->fType
& MF_OWNERDRAW
))
552 ** Experimentation under Windows reveals that an owner-drawn
553 ** menu is given the rectangle which includes the space it requested
554 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
555 ** and a popup-menu arrow. This is the value of lpitem->rect.
556 ** Windows will leave all drawing to the application except for
557 ** the popup-menu arrow. Windows always draws that itself, after
558 ** the menu owner has finished drawing.
562 dis
.CtlType
= ODT_MENU
;
564 dis
.itemID
= Item
->wID
;
565 dis
.itemData
= (DWORD
)Item
->dwItemData
;
567 if (0 != (Item
->fState
& MF_CHECKED
))
569 dis
.itemState
|= ODS_CHECKED
;
571 if (0 != (Item
->fState
& MF_GRAYED
))
573 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
575 if (0 != (Item
->fState
& MF_HILITE
))
577 dis
.itemState
|= ODS_SELECTED
;
579 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
580 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
582 dis
.rcItem
= Item
->Rect
;
583 DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
584 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
585 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
586 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
588 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
589 /* Fall through to draw popup-menu arrow */
592 DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
593 Item
->Rect
.right
, Item
->Rect
.bottom
);
595 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
602 if (0 == (Item
->fType
& MF_OWNERDRAW
))
604 if (Item
->fState
& MF_HILITE
)
608 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
612 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
617 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENU
));
621 SetBkMode(Dc
, TRANSPARENT
);
623 if (0 == (Item
->fType
& MF_OWNERDRAW
))
625 /* vertical separator */
626 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
630 rc
.bottom
= Height
- 3;
631 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
634 /* horizontal separator */
635 if (0 != (Item
->fType
& MF_SEPARATOR
))
640 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
641 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
648 /* helper lines for debugging */
649 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
650 SelectObject(Dc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
));
651 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
652 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
657 INT y
= Rect
.top
+ Rect
.bottom
;
658 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
659 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
661 if (0 == (Item
->fType
& MF_OWNERDRAW
))
663 /* Draw the check mark
666 * Custom checkmark bitmaps are monochrome but not always 1bpp.
668 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
669 if (NULL
!= bm
) /* we have a custom bitmap */
671 HDC DcMem
= CreateCompatibleDC(Dc
);
672 SelectObject(DcMem
, bm
);
673 BitBlt(Dc
, Rect
.left
, (y
- CheckBitmapHeight
) / 2,
674 CheckBitmapWidth
, CheckBitmapHeight
,
675 DcMem
, 0, 0, SRCCOPY
);
678 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
681 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
682 HDC DcMem
= CreateCompatibleDC(Dc
);
683 SelectObject(DcMem
, bm
);
684 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
685 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
686 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
687 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
688 BitBlt(Dc
, Rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
689 DcMem
, 0, 0, SRCCOPY
);
695 /* Draw the popup-menu arrow */
696 if (0 != (Item
->fType
& MF_POPUP
))
698 HDC DcMem
= CreateCompatibleDC(Dc
);
701 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
702 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
703 (y
- ArrowBitmapHeight
) / 2,
704 ArrowBitmapWidth
, ArrowBitmapHeight
,
705 DcMem
, 0, 0, SRCCOPY
);
706 SelectObject(DcMem
, OrigBitmap
);
710 Rect
.left
+= CheckBitmapWidth
;
711 Rect
.right
-= ArrowBitmapWidth
;
714 /* Done for owner-drawn */
715 if (0 != (Item
->fType
& MF_OWNERDRAW
))
720 /* Draw the item text or bitmap */
721 if (IS_BITMAP_ITEM(Item
->fType
))
723 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuBar
);
726 /* No bitmap - process text if present */
727 else if (IS_STRING_ITEM(Item
->fType
))
730 HFONT FontOld
= NULL
;
732 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
733 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
735 if (0 != (Item
->fState
& MFS_DEFAULT
))
737 FontOld
= SelectObject(Dc
, hMenuFontBold
);
742 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
743 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
746 Text
= (PWCHAR
) Item
->dwTypeData
;
747 for (i
= 0; L
'\0' != Text
[i
]; i
++)
749 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
755 if (0 != (Item
->fState
& MF_GRAYED
))
757 if (0 == (Item
->fState
& MF_HILITE
))
759 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
760 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
761 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
762 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
764 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
767 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
769 /* paint the shortcut text */
770 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
772 if (L
'\t' == Text
[i
])
774 Rect
.left
= Item
->XTab
;
775 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
779 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
782 if (0 != (Item
->fState
& MF_GRAYED
))
784 if (0 == (Item
->fState
& MF_HILITE
))
786 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
787 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
788 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
789 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
791 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
793 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
798 SelectObject(Dc
, FontOld
);
803 /***********************************************************************
806 * Paint a popup menu.
809 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
811 HBRUSH PrevBrush
= NULL
;
814 ROSMENUINFO MenuInfo
;
815 ROSMENUITEMINFO ItemInfo
;
818 DPRINT("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
820 GetClientRect(Wnd
, &Rect
);
822 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
823 && NULL
!= SelectObject(Dc
, hMenuFont
))
825 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
827 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
830 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
832 /* draw menu items */
834 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
836 MenuInitRosMenuItemInfo(&ItemInfo
);
838 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
840 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
842 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
843 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
847 MenuCleanupRosMenuItemInfo(&ItemInfo
);
852 SelectObject(Dc
, PrevBrush
);
857 static LRESULT WINAPI
858 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
860 DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
866 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
867 SetWindowLongW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
871 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
872 return MA_NOACTIVATE
;
877 BeginPaint(Wnd
, &ps
);
878 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongW(Wnd
, 0));
887 /* zero out global pointer in case resident popup window was destroyed. */
897 if (0 == GetWindowLongW(Wnd
, 0))
899 OutputDebugStringA("no menu to display\n");
904 SetWindowLongW(Wnd
, 0, 0);
908 case MM_SETMENUHANDLE
:
909 SetWindowLongW(Wnd
, 0, wParam
);
912 case MM_GETMENUHANDLE
:
913 return GetWindowLongW(Wnd
, 0);
916 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
922 /**********************************************************************
923 * MENUEX_ParseResource
925 * Parse an extended menu resource and add items to the menu.
926 * Return a pointer to the end of the resource.
928 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
930 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
938 mii
.cbSize
= sizeof(mii
);
939 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
940 mii
.fType
= GET_DWORD(res
);
941 res
+= sizeof(DWORD
);
942 mii
.fState
= GET_DWORD(res
);
943 res
+= sizeof(DWORD
);
944 mii
.wID
= GET_DWORD(res
);
945 res
+= sizeof(DWORD
);
946 resinfo
= GET_WORD(res
);
948 /* Align the text on a word boundary. */
949 res
+= (~((int)res
- 1)) & 1;
950 mii
.dwTypeData
= (LPWSTR
) res
;
951 res
+= (1 + wcslen(mii
.dwTypeData
)) * sizeof(WCHAR
);
952 /* Align the following fields on a dword boundary. */
953 res
+= (~((int)res
- 1)) & 3;
955 if (resinfo
& 1) /* Pop-up? */
957 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
958 res
+= sizeof(DWORD
);
959 mii
.hSubMenu
= CreatePopupMenu();
962 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
964 DestroyMenu(mii
.hSubMenu
);
967 mii
.fMask
|= MIIM_SUBMENU
;
968 mii
.fType
|= MF_POPUP
;
970 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
972 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
974 mii
.fType
|= MF_SEPARATOR
;
976 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
978 while (!(resinfo
& MF_END
));
983 /**********************************************************************
986 * Parse a standard menu resource and add items to the menu.
987 * Return a pointer to the end of the resource.
989 * NOTE: flags is equivalent to the mtOption field
991 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1000 flags
= GET_WORD(res
);
1002 /* remove MF_END flag before passing it to AppendMenu()! */
1003 end
= (flags
& MF_END
);
1004 if(end
) flags
^= MF_END
;
1006 res
+= sizeof(WORD
);
1007 if(!(flags
& MF_POPUP
))
1010 res
+= sizeof(WORD
);
1014 res
+= strlen(str
) + 1;
1016 res
+= (wcslen((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1017 if (flags
& MF_POPUP
)
1019 hSubMenu
= CreatePopupMenu();
1020 if(!hSubMenu
) return NULL
;
1021 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1024 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1026 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1028 else /* Not a popup */
1031 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1033 AppendMenuW(hMenu
, flags
, id
,
1034 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1043 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1047 hUser32
= GetModuleHandleW(L
"USER32");
1048 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
1049 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1056 NONCLIENTMETRICSW ncm
;
1058 /* get the menu font */
1059 if(!hMenuFont
|| !hMenuFontBold
)
1061 ncm
.cbSize
= sizeof(ncm
);
1062 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1064 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1068 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1069 if(hMenuFont
== NULL
)
1071 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1075 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1076 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1077 if(hMenuFontBold
== NULL
)
1079 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1088 /***********************************************************************
1091 * Calculate the size of the menu item and store it in ItemInfo->rect.
1093 static void FASTCALL
1094 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, HWND WndOwner
,
1095 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1098 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1100 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1102 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1104 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1107 ** Experimentation under Windows reveals that an owner-drawn
1108 ** menu is expected to return the size of the content part of
1109 ** the menu item, not including the checkmark nor the submenu
1110 ** arrow. Windows adds those values itself and returns the
1111 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1113 MEASUREITEMSTRUCT mis
;
1114 mis
.CtlType
= ODT_MENU
;
1116 mis
.itemID
= ItemInfo
->wID
;
1117 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1120 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1121 ItemInfo
->Rect
.right
+= mis
.itemWidth
;
1125 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1127 /* under at least win95 you seem to be given a standard
1128 height for the menu and the height value is ignored */
1130 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENU
) - 1;
1134 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1137 DPRINT("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1138 /* Fall through to get check/arrow width calculation. */
1141 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1143 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1149 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1150 if (0 != (ItemInfo
->fType
& MF_POPUP
))
1152 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1156 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1161 if (IS_BITMAP_ITEM(ItemInfo
->fType
))
1165 MenuGetBitmapItemSize((int) ItemInfo
->hbmpItem
, (DWORD
) ItemInfo
->hbmpItem
, &Size
);
1166 ItemInfo
->Rect
.right
+= Size
.cx
;
1167 ItemInfo
->Rect
.bottom
+= Size
.cy
;
1169 /* Leave space for the sunken border */
1170 ItemInfo
->Rect
.right
+= 2;
1171 ItemInfo
->Rect
.bottom
+= 2;
1173 /* Special case: Minimize button doesn't have a space behind it. */
1174 if (ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1175 ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1176 ItemInfo
->Rect
.right
-= 1;
1179 /* it must be a text item - unless it's the system menu */
1180 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && IS_STRING_ITEM(ItemInfo
->fType
))
1184 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1185 wcslen((LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1187 ItemInfo
->Rect
.right
+= Size
.cx
;
1188 ItemInfo
->Rect
.bottom
+= max(Size
.cy
, GetSystemMetrics(SM_CYMENU
) - 1);
1193 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1195 else if ((p
= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\t' )) != NULL
)
1197 /* Item contains a tab (only meaningful in popup menus) */
1198 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1199 (int)(p
- (LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1200 ItemInfo
->XTab
= CheckBitmapWidth
+ MENU_TAB_SPACE
+ Size
.cx
;
1201 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1205 if (NULL
!= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\b'))
1207 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1209 ItemInfo
->XTab
= ItemInfo
->Rect
.right
- CheckBitmapWidth
1214 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1217 /***********************************************************************
1218 * MenuPopupMenuCalcSize
1220 * Calculate the size of a popup menu.
1222 static void FASTCALL
1223 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1225 ROSMENUITEMINFO ItemInfo
;
1228 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1230 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1231 if (0 == MenuInfo
->MenuItemCount
)
1233 MenuSetRosMenuInfo(MenuInfo
);
1238 SelectObject(Dc
, hMenuFont
);
1243 MenuInitRosMenuItemInfo(&ItemInfo
);
1244 while (Start
< MenuInfo
->MenuItemCount
)
1249 MaxTab
= MaxTabWidth
= 0;
1251 /* Parse items until column break or end of menu */
1252 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1254 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1256 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1257 MenuSetRosMenuInfo(MenuInfo
);
1261 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1266 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1267 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1269 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1270 MenuSetRosMenuInfo(MenuInfo
);
1274 if (0 != (ItemInfo
.fType
& MF_MENUBARBREAK
))
1278 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1279 OrgY
= ItemInfo
.Rect
.bottom
;
1280 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1282 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1283 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1287 /* Finish the column (set all items to the largest width found) */
1288 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1291 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1293 ItemInfo
.Rect
.right
= MaxX
;
1294 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1296 ItemInfo
.XTab
= MaxTab
;
1298 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1302 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1305 MenuInfo
->Width
= MaxX
;
1307 /* space for 3d border */
1308 MenuInfo
->Height
+= 2;
1309 MenuInfo
->Width
+= 2;
1311 ReleaseDC(NULL
, Dc
);
1312 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1313 MenuSetRosMenuInfo(MenuInfo
);
1316 /***********************************************************************
1317 * MenuMenuBarCalcSize
1319 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1320 * height is off by 1 pixel which causes lengthy window relocations when
1321 * active document window is maximized/restored.
1323 * Calculate the size of the menu bar.
1325 static void FASTCALL
1326 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1328 ROSMENUITEMINFO ItemInfo
;
1329 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1331 if (NULL
== Rect
|| NULL
== MenuInfo
)
1335 if (0 == MenuInfo
->MenuItemCount
)
1340 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1341 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1342 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1343 MenuInfo
->Height
= 0;
1344 MaxY
= Rect
->top
+ 1;
1347 MenuInitRosMenuItemInfo(&ItemInfo
);
1348 while (Start
< MenuInfo
->MenuItemCount
)
1350 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1352 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1358 /* Parse items until line break or end of menu */
1359 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1361 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1366 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1371 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1372 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1373 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1375 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1379 if (ItemInfo
.Rect
.right
> Rect
->right
)
1387 ItemInfo
.Rect
.right
= Rect
->right
;
1390 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1391 OrgX
= ItemInfo
.Rect
.right
;
1392 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1394 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1396 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1402 /* FIXME: Is this really needed? */
1404 /* Finish the line (set all items to the largest height found) */
1407 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1409 ItemInfo
.Rect
.bottom
= MaxY
;
1410 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1419 Rect
->bottom
= MaxY
;
1420 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1421 MenuSetRosMenuInfo(MenuInfo
);
1425 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1426 /* the last item (if several lines, only move the last line) */
1427 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1429 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1432 OrgY
= ItemInfo
.Rect
.top
;
1434 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1440 if (ItemInfo
.Rect
.top
!= OrgY
)
1442 break; /* Other line */
1444 if (OrgX
<= ItemInfo
.Rect
.right
)
1446 break; /* Too far right already */
1448 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1449 ItemInfo
.Rect
.right
= OrgX
;
1450 OrgX
= ItemInfo
.Rect
.left
;
1451 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1452 if (HelpPos
+ 1 <= i
&&
1453 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1455 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1461 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1464 /***********************************************************************
1465 * DrawMenuBarTemp (USER32.@)
1469 * called by W98SE desk.cpl Control Panel Applet
1471 * Not 100% sure about the param names, but close.
1476 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1478 ROSMENUINFO MenuInfo
;
1479 ROSMENUITEMINFO ItemInfo
;
1481 HFONT FontOld
= NULL
;
1485 Menu
= GetMenu(Wnd
);
1493 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1495 return GetSystemMetrics(SM_CYMENU
);
1498 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1500 FontOld
= SelectObject(DC
, Font
);
1502 if (0 == MenuInfo
.Height
)
1504 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1507 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1509 FillRect(DC
, Rect
, GetSysColorBrush(COLOR_MENU
));
1511 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1512 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1513 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1515 if (0 == MenuInfo
.MenuItemCount
)
1517 SelectObject(DC
, FontOld
);
1518 return GetSystemMetrics(SM_CYMENU
);
1521 MenuInitRosMenuItemInfo(&ItemInfo
);
1522 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1524 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1526 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1527 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1530 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1532 SelectObject(DC
, FontOld
);
1534 return MenuInfo
.Height
;
1538 /***********************************************************************
1541 * Paint a menu bar. Returns the height of the menu bar.
1542 * called from [windows/nonclient.c]
1544 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1546 ROSMENUINFO MenuInfo
;
1547 HFONT FontOld
= NULL
;
1548 HMENU Menu
= GetMenu(Wnd
);
1550 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1552 return GetSystemMetrics(SM_CYMENU
);
1557 FontOld
= SelectObject(DC
, hMenuFont
);
1559 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1561 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1563 if (NULL
!= FontOld
)
1565 SelectObject(DC
, FontOld
);
1567 return MenuInfo
.Height
;
1571 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1575 /***********************************************************************
1578 static BOOL FASTCALL
1579 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1581 DPRINT("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1585 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1586 if (0 == (Flags
& TPM_NONOTIFY
))
1588 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1591 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1593 if (0 == (Flags
& TPM_NONOTIFY
))
1595 ROSMENUINFO MenuInfo
;
1597 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1599 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 == MenuInfo
.Height
)
1601 /* app changed/recreated menu bar entries in WM_INITMENU
1602 Recalculate menu sizes else clicks will not work */
1603 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1604 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1613 /***********************************************************************
1616 * Display a popup menu.
1618 static BOOL FASTCALL
1619 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1620 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1622 ROSMENUINFO MenuInfo
;
1623 ROSMENUITEMINFO ItemInfo
;
1626 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1627 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1629 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1634 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1636 MenuInitRosMenuItemInfo(&ItemInfo
);
1637 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1639 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1640 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1642 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1643 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1646 /* store the owner for DrawItem */
1647 MenuInfo
.WndOwner
= WndOwner
;
1648 MenuSetRosMenuInfo(&MenuInfo
);
1650 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1652 /* adjust popup menu pos so that it fits within the desktop */
1654 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1655 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1657 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1661 X
-= Width
- XAnchor
;
1663 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1665 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1673 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1677 Y
-= Height
+ YAnchor
;
1679 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1681 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1690 /* NOTE: In Windows, top menu popup is not owned. */
1691 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1692 WS_POPUP
, X
, Y
, Width
, Height
,
1693 WndOwner
, 0, (HINSTANCE
) GetWindowLongW(WndOwner
, GWL_HINSTANCE
),
1694 (LPVOID
) MenuInfo
.Self
);
1695 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1699 if (NULL
== TopPopup
)
1701 TopPopup
= MenuInfo
.Wnd
;
1704 /* Display the window */
1705 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1706 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1707 UpdateWindow(MenuInfo
.Wnd
);
1712 /***********************************************************************
1715 * Find a Sub menu. Return the position of the submenu, and modifies
1716 * *hmenu in case it is found in another sub-menu.
1717 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1719 static UINT FASTCALL
1720 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1722 ROSMENUINFO MenuInfo
;
1723 ROSMENUITEMINFO ItemInfo
;
1728 if ((HMENU
) 0xffff == *Menu
1729 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1731 return NO_SELECTED_ITEM
;
1734 MenuInitRosMenuItemInfo(&ItemInfo
);
1735 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1737 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1739 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1740 return NO_SELECTED_ITEM
;
1742 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1746 if (ItemInfo
.hSubMenu
== SubTarget
)
1748 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1751 SubMenu
= ItemInfo
.hSubMenu
;
1752 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1753 if (NO_SELECTED_ITEM
!= Pos
)
1759 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1761 return NO_SELECTED_ITEM
;
1764 /***********************************************************************
1767 static void FASTCALL
1768 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1769 BOOL SendMenuSelect
, HMENU TopMenu
)
1772 ROSMENUITEMINFO ItemInfo
;
1773 ROSMENUINFO TopMenuInfo
;
1776 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1778 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1783 if (MenuInfo
->FocusedItem
== Index
)
1788 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1790 Dc
= GetDC(MenuInfo
->Wnd
);
1794 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1797 if (NULL
== TopPopup
)
1799 TopPopup
= MenuInfo
->Wnd
;
1802 SelectObject(Dc
, hMenuFont
);
1803 MenuInitRosMenuItemInfo(&ItemInfo
);
1805 /* Clear previous highlighted item */
1806 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1808 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1810 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1811 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1813 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1814 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1818 /* Highlight new item (if any) */
1819 MenuInfo
->FocusedItem
= Index
;
1820 MenuSetRosMenuInfo(MenuInfo
);
1821 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1823 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1825 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1827 ItemInfo
.fState
|= MF_HILITE
;
1828 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1829 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
1830 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1835 SendMessageW(WndOwner
, WM_MENUSELECT
,
1836 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
1837 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1838 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
1842 else if (SendMenuSelect
)
1844 if (NULL
!= TopMenu
)
1846 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
1847 if (NO_SELECTED_ITEM
!= Pos
)
1849 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
1850 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
1852 SendMessageW(WndOwner
, WM_MENUSELECT
,
1853 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1855 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1862 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1863 ReleaseDC(MenuInfo
->Wnd
, Dc
);
1866 /***********************************************************************
1869 * Moves currently selected item according to the Offset parameter.
1870 * If there is no selection then it should select the last item if
1871 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1873 static void FASTCALL
1874 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1877 ROSMENUITEMINFO ItemInfo
;
1880 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1882 /* Prevent looping */
1883 if (0 == MenuInfo
->MenuItemCount
|| 0 == Offset
)
1885 else if (Offset
< -1)
1887 else if (Offset
> 1)
1890 MenuInitRosMenuItemInfo(&ItemInfo
);
1892 OrigPos
= MenuInfo
->FocusedItem
;
1893 if (OrigPos
== NO_SELECTED_ITEM
) /* NO_SELECTED_ITEM is not -1 ! */
1900 i
= MenuInfo
->FocusedItem
;
1907 /* Clip and wrap around */
1910 i
= MenuInfo
->MenuItemCount
- 1;
1912 else if (i
>= MenuInfo
->MenuItemCount
)
1916 /* If this is a good candidate; */
1917 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1918 0 == (ItemInfo
.fType
& MF_SEPARATOR
) &&
1919 0 == (ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
1921 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1922 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1925 } while (i
!= OrigPos
);
1928 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1931 /***********************************************************************
1932 * MenuInitSysMenuPopup
1934 * Grey the appropriate items in System menu.
1937 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
1945 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1946 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1947 Gray
= 0 != (Style
& WS_MAXIMIZE
);
1948 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1949 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
1950 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1951 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
1952 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1953 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1954 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1955 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
1957 /* The menu item must keep its state if it's disabled */
1960 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
1963 /* Set default menu item */
1964 if(Style
& WS_MINIMIZE
)
1966 DefItem
= SC_RESTORE
;
1970 if(HitTest
== HTCAPTION
)
1972 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
1980 mii
.cbSize
= sizeof(MENUITEMINFOW
);
1981 mii
.fMask
= MIIM_STATE
;
1982 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
1983 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
1988 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
1991 /***********************************************************************
1994 * Display the sub-menu of the selected item of this menu.
1995 * Return the handle of the submenu, or menu if no submenu to display.
1997 static HMENU FASTCALL
1998 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2000 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2002 ROSMENUITEMINFO ItemInfo
;
2003 ROSMENUINFO SubMenuInfo
;
2007 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2009 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2011 return MenuInfo
->Self
;
2014 MenuInitRosMenuItemInfo(&ItemInfo
);
2015 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2017 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2018 return MenuInfo
->Self
;
2020 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2022 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2023 return MenuInfo
->Self
;
2026 /* message must be sent before using item,
2027 because nearly everything may be changed by the application ! */
2029 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2030 if (0 == (Flags
& TPM_NONOTIFY
))
2032 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2033 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2036 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2038 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2039 return MenuInfo
->Self
;
2041 Rect
= ItemInfo
.Rect
;
2043 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2044 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2046 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2048 Dc
= GetDC(MenuInfo
->Wnd
);
2052 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2055 SelectObject(Dc
, hMenuFont
);
2057 ItemInfo
.fState
|= MF_HILITE
;
2058 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2059 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2060 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2061 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2064 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2065 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2067 ItemInfo
.Rect
= Rect
;
2070 ItemInfo
.fState
|= MF_MOUSESELECT
;
2072 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2074 if (IS_SYSTEM_MENU(MenuInfo
))
2076 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2077 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2079 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2080 Rect
.top
= Rect
.bottom
;
2081 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2082 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2086 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2087 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2089 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2090 Rect
.top
+= ItemInfo
.Rect
.top
;
2091 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2092 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
;
2096 Rect
.left
+= ItemInfo
.Rect
.left
;
2097 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2098 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2099 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2103 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2104 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2105 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2107 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2110 Ret
= ItemInfo
.hSubMenu
;
2111 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2116 /***********************************************************************
2119 * Hide the sub-popup menus of this menu.
2121 static void FASTCALL
2122 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2124 ROSMENUINFO SubMenuInfo
;
2125 ROSMENUITEMINFO ItemInfo
;
2127 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2129 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2131 MenuInitRosMenuItemInfo(&ItemInfo
);
2132 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2133 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2134 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2136 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2139 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2140 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2141 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2143 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2144 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2145 DestroyWindow(SubMenuInfo
.Wnd
);
2146 SubMenuInfo
.Wnd
= NULL
;
2147 MenuSetRosMenuInfo(&SubMenuInfo
);
2152 /***********************************************************************
2153 * MenuSwitchTracking
2155 * Helper function for menu navigation routines.
2157 static void FASTCALL
2158 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2160 ROSMENUINFO TopMenuInfo
;
2162 DPRINT("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2164 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2165 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2166 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2168 /* both are top level menus (system and menu-bar) */
2169 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2170 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2171 Mt
->TopMenu
= PtMenuInfo
->Self
;
2175 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2178 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2181 /***********************************************************************
2182 * MenuExecFocusedItem
2184 * Execute a menu item (for instance when user pressed Enter).
2185 * Return the wID of the executed item. Otherwise, -1 indicating
2186 * that no menu item was executed;
2187 * Have to receive the flags for the TrackPopupMenu options to avoid
2188 * sending unwanted message.
2192 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2194 ROSMENUITEMINFO ItemInfo
;
2197 DPRINT("%p menu=%p\n", Mt
, MenuInfo
);
2199 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2204 MenuInitRosMenuItemInfo(&ItemInfo
);
2205 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2207 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2211 DPRINT("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2213 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2215 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2216 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2218 /* If TPM_RETURNCMD is set you return the id, but
2219 do not send a message to the owner */
2220 if (0 == (Flags
& TPM_RETURNCMD
))
2222 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2224 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2225 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2229 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2233 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2239 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2245 /***********************************************************************
2248 * Return TRUE if we can go on with menu tracking.
2250 static BOOL FASTCALL
2251 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2254 ROSMENUINFO MenuInfo
;
2255 ROSMENUITEMINFO Item
;
2257 DPRINT("%x PtMenu=%p\n", Mt
, PtMenu
);
2261 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2265 if (IS_SYSTEM_MENU(&MenuInfo
))
2271 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2273 MenuInitRosMenuItemInfo(&Item
);
2274 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2276 MenuCleanupRosMenuItemInfo(&Item
);
2280 if (!(Item
.fType
& MF_SEPARATOR
) &&
2281 !(Item
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2283 if (MenuInfo
.FocusedItem
!= Index
)
2285 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2288 /* If the popup menu is not already "popped" */
2289 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2291 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2295 MenuCleanupRosMenuItemInfo(&Item
);
2300 /* else the click was on the menu bar, finish the tracking */
2305 /***********************************************************************
2308 * Return the value of MenuExecFocusedItem if
2309 * the selected item was not a popup. Else open the popup.
2310 * A -1 return value indicates that we go on with menu tracking.
2314 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2317 ROSMENUINFO MenuInfo
;
2318 ROSMENUITEMINFO ItemInfo
;
2320 DPRINT("%p hmenu=%x\n", Mt
, PtMenu
);
2325 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2330 if (! IS_SYSTEM_MENU(&MenuInfo
))
2332 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2334 MenuInitRosMenuItemInfo(&ItemInfo
);
2335 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2336 MenuInfo
.FocusedItem
== Id
)
2338 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2340 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2341 return MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2343 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2345 /* If we are dealing with the top-level menu */
2346 /* and this is a click on an already "popped" item: */
2347 /* Stop the menu tracking and close the opened submenus */
2348 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2350 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2354 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2355 MenuInfo
.TimeToHide
= TRUE
;
2356 MenuSetRosMenuInfo(&MenuInfo
);
2362 /***********************************************************************
2365 * Walks menu chain trying to find a menu pt maps to.
2367 static HMENU FASTCALL
2368 MenuPtMenu(HMENU Menu
, POINT Pt
)
2370 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2371 ROSMENUINFO MenuInfo
;
2372 ROSMENUITEMINFO ItemInfo
;
2376 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2381 /* try subpopup first (if any) */
2382 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2384 MenuInitRosMenuItemInfo(&ItemInfo
);
2385 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2386 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2387 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2389 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2392 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2396 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2399 /* check the current window (avoiding WM_HITTEST) */
2400 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2401 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2403 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2408 else if (HTSYSMENU
== Ht
)
2410 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2412 else if (HTMENU
== Ht
)
2414 Ret
= GetMenu(MenuInfo
.Wnd
);
2420 /***********************************************************************
2423 * Return TRUE if we can go on with menu tracking.
2425 static BOOL FASTCALL
2426 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2429 ROSMENUINFO MenuInfo
;
2430 ROSMENUITEMINFO ItemInfo
;
2434 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2438 if (IS_SYSTEM_MENU(&MenuInfo
))
2444 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2449 Index
= NO_SELECTED_ITEM
;
2452 if (NO_SELECTED_ITEM
== Index
)
2454 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2455 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2457 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2461 else if (MenuInfo
.FocusedItem
!= Index
)
2463 MenuInitRosMenuItemInfo(&ItemInfo
);
2464 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, Index
, &ItemInfo
) &&
2465 !(ItemInfo
.fType
& MF_SEPARATOR
) &&
2466 !(ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2468 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2469 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2471 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2477 /******************************************************************************
2479 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2481 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2484 PROSMENUITEMINFO MenuItems
;
2486 i
= MenuInfo
->FocusedItem
;
2487 if (NO_SELECTED_ITEM
== i
)
2492 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2494 return NO_SELECTED_ITEM
;
2497 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2499 if (0 != (MenuItems
[i
].fType
& MF_MENUBARBREAK
))
2505 return NO_SELECTED_ITEM
;
2508 /******************************************************************************
2510 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2512 static UINT FASTCALL
2513 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2516 PROSMENUITEMINFO MenuItems
;
2518 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2520 return NO_SELECTED_ITEM
;
2523 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2525 return NO_SELECTED_ITEM
;
2528 /* Find the start of the column */
2530 for (i
= MenuInfo
->FocusedItem
;
2531 0 != i
&& 0 == (MenuItems
[i
].fType
& MF_MENUBARBREAK
);
2539 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2540 return NO_SELECTED_ITEM
;
2543 for (--i
; 0 != i
; --i
)
2545 if (MenuItems
[i
].fType
& MF_MENUBARBREAK
)
2551 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2552 DPRINT("ret %d.\n", i
);
2557 /***********************************************************************
2560 * Return the handle of the selected sub-popup menu (if any).
2562 static HMENU FASTCALL
2563 MenuGetSubPopup(HMENU Menu
)
2565 ROSMENUINFO MenuInfo
;
2566 ROSMENUITEMINFO ItemInfo
;
2568 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2569 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2574 MenuInitRosMenuItemInfo(&ItemInfo
);
2575 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2577 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2580 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2582 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2583 return ItemInfo
.hSubMenu
;
2586 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2590 /***********************************************************************
2593 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2595 static LRESULT FASTCALL
2596 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2598 ROSMENUINFO TopMenuInfo
;
2599 ROSMENUINFO MenuInfo
;
2601 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2603 return (LRESULT
) FALSE
;
2606 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2607 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2609 MDINEXTMENU NextMenu
;
2614 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2615 NextMenu
.hmenuNext
= NULL
;
2616 NextMenu
.hwndNext
= NULL
;
2617 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2619 DPRINT("%p [%p] -> %p [%p]\n",
2620 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2622 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2624 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2625 NewWnd
= Mt
->OwnerWnd
;
2626 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2628 /* switch to the menu bar */
2630 if (0 != (Style
& WS_CHILD
)
2631 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2638 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2642 Id
= MenuInfo
.MenuItemCount
- 1;
2645 else if (0 != (Style
& WS_SYSMENU
))
2647 /* switch to the system menu */
2648 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2655 else /* application returned a new menu to switch to */
2657 NewMenu
= NextMenu
.hmenuNext
;
2658 NewWnd
= NextMenu
.hwndNext
;
2660 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2662 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2664 if (0 != (Style
& WS_SYSMENU
)
2665 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2667 /* get the real system menu */
2668 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2670 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2672 /* FIXME: Not sure what to do here;
2673 * perhaps try to track NewMenu as a popup? */
2675 DPRINT(" -- got confused.\n");
2685 if (NewMenu
!= Mt
->TopMenu
)
2687 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2689 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2691 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2695 if (NewWnd
!= Mt
->OwnerWnd
)
2697 Mt
->OwnerWnd
= NewWnd
;
2698 SetCapture(Mt
->OwnerWnd
);
2699 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2702 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2703 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2705 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2714 /***********************************************************************
2717 * The idea is not to show the popup if the next input message is
2718 * going to hide it anyway.
2720 static BOOL FASTCALL
2721 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2725 Msg
.hwnd
= Mt
->OwnerWnd
;
2727 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2728 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2733 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2734 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2736 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2737 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2738 if (WM_KEYDOWN
== Msg
.message
2739 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2741 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2748 /* failures go through this */
2749 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2754 /***********************************************************************
2757 * Handle a VK_ESCAPE key event in a menu.
2759 static BOOL FASTCALL
2760 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2762 BOOL EndMenu
= TRUE
;
2763 ROSMENUINFO MenuInfo
;
2764 HMENU MenuTmp
, MenuPrev
;
2766 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2768 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2769 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2771 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2773 /* close topmost popup */
2774 while (MenuTmp
!= Mt
->CurrentMenu
)
2777 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2780 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2782 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2784 Mt
->CurrentMenu
= MenuPrev
;
2792 /***********************************************************************
2795 * Handle a VK_LEFT key event in a menu.
2797 static void FASTCALL
2798 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2800 ROSMENUINFO MenuInfo
;
2801 ROSMENUINFO TopMenuInfo
;
2802 ROSMENUINFO PrevMenuInfo
;
2803 HMENU MenuTmp
, MenuPrev
;
2806 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2808 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2813 /* Try to move 1 column left (if possible) */
2814 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2816 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2818 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2823 /* close topmost popup */
2824 while (MenuTmp
!= Mt
->CurrentMenu
)
2827 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2830 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
2834 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
2835 Mt
->CurrentMenu
= MenuPrev
;
2837 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2841 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
2843 /* move menu bar selection if no more popups are left */
2845 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
2847 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
2850 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2852 /* A sublevel menu was displayed - display the next one
2853 * unless there is another displacement coming up */
2855 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2856 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2858 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
2865 /***********************************************************************
2868 * Handle a VK_RIGHT key event in a menu.
2870 static void FASTCALL
2871 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
2874 ROSMENUINFO MenuInfo
;
2875 ROSMENUINFO CurrentMenuInfo
;
2878 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2879 Mt
->CurrentMenu
, Mt
->TopMenu
);
2881 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2885 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
2887 /* If already displaying a popup, try to display sub-popup */
2889 MenuTmp
= Mt
->CurrentMenu
;
2890 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2892 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
2895 /* if subpopup was displayed then we are done */
2896 if (MenuTmp
!= Mt
->CurrentMenu
)
2902 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2907 /* Check to see if there's another column */
2908 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
2910 DPRINT("Going to %d.\n", NextCol
);
2911 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2913 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
2918 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
2920 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2922 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
2923 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
2930 /* try to move to the next item */
2931 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
2933 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
2936 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2938 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2939 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2941 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
2948 /***********************************************************************
2951 * Find the menu item selected by a key press.
2952 * Return item id, -1 if none, -2 if we should close the menu.
2954 static UINT FASTCALL
2955 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
2956 WCHAR Key
, BOOL ForceMenuChar
)
2958 ROSMENUINFO SysMenuInfo
;
2959 PROSMENUITEMINFO Items
, ItemInfo
;
2963 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
2965 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
2967 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
2969 MenuInfo
= &SysMenuInfo
;
2977 if (NULL
!= MenuInfo
)
2979 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
2983 if (! ForceMenuChar
)
2985 Key
= towupper(Key
);
2987 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
2989 if (IS_STRING_ITEM(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
2991 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
2994 p
= wcschr(p
+ 2, '&');
2996 while (NULL
!= p
&& L
'&' == p
[1]);
2997 if (NULL
!= p
&& (towupper(p
[1]) == Key
))
3005 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3006 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3007 if (2 == HIWORD(MenuChar
))
3009 return LOWORD(MenuChar
);
3011 if (1 == HIWORD(MenuChar
))
3020 /***********************************************************************
3023 * Menu tracking code.
3026 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3027 HWND Wnd
, const RECT
*Rect
)
3030 ROSMENUINFO MenuInfo
;
3031 ROSMENUITEMINFO ItemInfo
;
3033 INT ExecutedMenuId
= -1;
3035 BOOL EnterIdleSent
= FALSE
;
3038 Mt
.CurrentMenu
= Menu
;
3044 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3045 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3046 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3049 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3054 if (0 != (Flags
& TPM_BUTTONDOWN
))
3056 /* Get the result in order to start the tracking or not */
3057 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3058 fEndMenu
= ! fRemove
;
3061 SetCapture(Mt
.OwnerWnd
);
3062 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3066 /* we have to keep the message in the queue until it's
3067 * clear that menu loop is not over yet. */
3071 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3073 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3077 /* remove the message from the queue */
3078 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3082 if (! EnterIdleSent
)
3084 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3085 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3086 EnterIdleSent
= TRUE
;
3087 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3093 /* check if EndMenu() tried to cancel us, by posting this message */
3094 if (WM_CANCELMODE
== Msg
.message
)
3096 /* we are now out of the loop */
3099 /* remove the message from the queue */
3100 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3102 /* break out of internal loop, ala ESCAPE */
3106 TranslateMessage(&Msg
);
3109 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3111 EnterIdleSent
= FALSE
;
3115 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3118 * Use the mouse coordinates in lParam instead of those in the MSG
3119 * struct to properly handle synthetic messages. They are already
3120 * in screen coordinates.
3122 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3123 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3125 /* Find a menu for this mouse event */
3126 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3130 /* no WM_NC... messages in captured state */
3132 case WM_RBUTTONDBLCLK
:
3133 case WM_RBUTTONDOWN
:
3134 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3139 case WM_LBUTTONDBLCLK
:
3140 case WM_LBUTTONDOWN
:
3141 /* If the message belongs to the menu, removes it from the queue */
3142 /* Else, end menu tracking */
3143 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3144 fEndMenu
= ! fRemove
;
3148 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3154 /* Check if a menu was selected by the mouse */
3157 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3159 /* End the loop if ExecutedMenuId is an item ID */
3160 /* or if the job was done (ExecutedMenuId = 0). */
3161 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3165 /* No menu was selected by the mouse */
3166 /* if the function was called by TrackPopupMenu, continue
3167 with the menu tracking. If not, stop it */
3168 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3175 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3179 } /* switch(Msg.message) - mouse */
3181 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3183 fRemove
= TRUE
; /* Keyboard messages are always removed */
3191 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3193 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3199 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3201 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3202 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3206 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3207 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3209 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3211 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3213 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3217 else /* otherwise try to move selection */
3219 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3225 MenuKeyLeft(&Mt
, Flags
);
3229 MenuKeyRight(&Mt
, Flags
);
3233 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3239 hi
.cbSize
= sizeof(HELPINFO
);
3240 hi
.iContextType
= HELPINFO_MENUITEM
;
3241 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3243 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3249 MenuInitRosMenuItemInfo(&ItemInfo
);
3250 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3251 MenuInfo
.FocusedItem
,
3254 hi
.iCtrlId
= ItemInfo
.wID
;
3260 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3263 hi
.hItemHandle
= Menu
;
3264 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3265 hi
.MousePos
= Msg
.pt
;
3266 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3273 break; /* WM_KEYDOWN */
3278 DbgPrint("Menu.c WM_SYSKEYDOWN wPram %d\n",Msg
.wParam
);
3286 break; /* WM_SYSKEYDOWN */
3292 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3296 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3298 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3299 fEndMenu
= (ExecutedMenuId
!= -1);
3303 /* Hack to avoid control chars. */
3304 /* We will find a better way real soon... */
3305 if (Msg
.wParam
< 32)
3310 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3311 LOWORD(Msg
.wParam
), FALSE
);
3312 if ((UINT
) -2 == Pos
)
3316 else if ((UINT
) -1 == Pos
)
3322 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3323 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3324 fEndMenu
= (-1 != ExecutedMenuId
);
3328 } /* switch(msg.message) - kbd */
3332 DispatchMessageW(&Msg
);
3340 /* finally remove message from the queue */
3342 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3344 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3348 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3352 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3353 SetCapture(NULL
); /* release the capture */
3355 /* If dropdown is still painted and the close box is clicked on
3356 then the menu will be destroyed as part of the DispatchMessage above.
3357 This will then invalidate the menu handle in Mt.hTopMenu. We should
3358 check for this first. */
3359 if (IsMenu(Mt
.TopMenu
))
3361 if (IsWindow(Mt
.OwnerWnd
))
3363 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3365 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3367 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3369 DestroyWindow(MenuInfo
.Wnd
);
3370 MenuInfo
.Wnd
= NULL
;
3372 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3375 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3378 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3380 /* Reset the variable for hiding menu */
3381 MenuInfo
.TimeToHide
= FALSE
;
3382 MenuSetRosMenuInfo(&MenuInfo
);
3386 /* The return value is only used by TrackPopupMenu */
3387 return (-1 != ExecutedMenuId
) ? ExecutedMenuId
: 0;
3390 /***********************************************************************
3393 static BOOL FASTCALL
3394 MenuExitTracking(HWND Wnd
)
3396 DPRINT("hwnd=%p\n", Wnd
);
3398 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3405 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3407 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3408 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3410 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3414 /* map point to parent client coordinates */
3415 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3416 if (Parent
!= GetDesktopWindow())
3418 ScreenToClient(Parent
, &Pt
);
3421 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3422 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3423 MenuExitTracking(Wnd
);
3429 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
3433 /* FUNCTIONS *****************************************************************/
3436 MenuIsStringItem(ULONG TypeData)
3438 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3446 AppendMenuA(HMENU hMenu
,
3448 UINT_PTR uIDNewItem
,
3451 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3460 AppendMenuW(HMENU hMenu
,
3462 UINT_PTR uIDNewItem
,
3465 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3474 CheckMenuItem(HMENU hmenu
,
3478 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3486 CheckMenuRadioItem(HMENU hmenu
,
3493 PROSMENUITEMINFO Items
;
3497 mi
.cbSize
= sizeof(MENUINFO
);
3501 if(idFirst
> idLast
) return ret
;
3503 if(!NtUserMenuInfo(hmenu
, &mi
, FALSE
)) return ret
;
3505 if(MenuGetAllRosMenuItemInfo(mi
.Self
, &Items
) <= 0) return ret
;
3507 for (i
= 0 ; i
< mi
.MenuItemCount
; i
++)
3509 if (0 != (Items
[i
].fType
& MF_MENUBARBREAK
)) break;
3510 if ( i
>= idFirst
&& i
<= idLast
)
3514 Items
[i
].fType
|= MFT_RADIOCHECK
;
3515 Items
[i
].fState
|= MFS_CHECKED
;
3519 Items
[i
].fType
&= ~MFT_RADIOCHECK
;
3520 Items
[i
].fState
&= ~MFS_CHECKED
;
3522 if(!MenuSetRosMenuItemInfo(mi
.Self
, i
,&Items
[i
]))
3525 if ( i
== mi
.MenuItemCount
) ret
= TRUE
;
3527 MenuCleanupRosMenuItemInfo(Items
);
3539 return NtUserCreateMenu(FALSE
);
3547 CreatePopupMenu(VOID
)
3550 return NtUserCreateMenu(TRUE
);
3558 DeleteMenu(HMENU hMenu
,
3562 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
3570 DestroyMenu(HMENU hMenu
)
3572 return NtUserDestroyMenu(hMenu
);
3580 DrawMenuBar(HWND hWnd
)
3582 return (BOOL
)NtUserCallHwndLock(hWnd
, HWNDLOCK_ROUTINE_DRAWMENUBAR
);
3590 EnableMenuItem(HMENU hMenu
,
3594 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
3604 guii
.cbSize
= sizeof(GUITHREADINFO
);
3605 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
3607 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
3619 return NtUserGetMenu(hWnd
);
3627 GetMenuBarInfo(HWND hwnd
,
3632 return (BOOL
)NtUserGetMenuBarInfo(hwnd
, idObject
, idItem
, pmbi
);
3640 GetMenuCheckMarkDimensions(VOID
)
3642 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
3643 GetSystemMetrics(SM_CYMENUCHECK
)));
3651 GetMenuDefaultItem(HMENU hMenu
,
3655 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
3663 GetMenuInfo(HMENU hmenu
,
3669 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
3672 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
3673 mi
.cbSize
= sizeof(MENUINFO
);
3674 mi
.fMask
= lpcmi
->fMask
;
3676 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
3678 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
3687 GetMenuItemCount(HMENU Menu
)
3689 ROSMENUINFO MenuInfo
;
3691 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
3699 GetMenuItemID(HMENU hMenu
,
3702 ROSMENUITEMINFO mii
;
3704 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3705 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
3707 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
3712 if (NULL
!= mii
.hSubMenu
)
3733 LPMENUITEMINFOA mii
)
3738 if (mii
->cbSize
!= sizeof(MENUITEMINFOA
) &&
3739 mii
->cbSize
!= sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
))
3741 SetLastError(ERROR_INVALID_PARAMETER
);
3745 if ((mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)) == 0)
3747 /* No text requested, just pass on */
3748 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3751 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
3752 AnsiBuffer
= mii
->dwTypeData
;
3754 if (AnsiBuffer
!= NULL
)
3756 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
3757 miiW
.cch
* sizeof(WCHAR
));
3758 if (miiW
.dwTypeData
== NULL
)
3762 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)&miiW
, FALSE
))
3764 HeapFree(GetProcessHeap(), 0, miiW
.dwTypeData
);
3768 if (AnsiBuffer
!= NULL
)
3770 if (IS_STRING_ITEM(miiW
.fType
))
3772 WideCharToMultiByte(CP_ACP
, 0, miiW
.dwTypeData
, miiW
.cch
, AnsiBuffer
,
3773 mii
->cch
, NULL
, NULL
);
3775 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
3778 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
);
3781 mii
->dwTypeData
= AnsiBuffer
;
3782 mii
->cch
= strlen(AnsiBuffer
);
3796 LPMENUITEMINFOW mii
)
3798 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3806 GetMenuItemRect(HWND hWnd
,
3811 return NtUserGetMenuItemRect( hWnd
, hMenu
, uItem
, lprcItem
);
3825 ROSMENUINFO MenuInfo
;
3826 ROSMENUITEMINFO mii
;
3828 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3829 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
3830 mii
.dwTypeData
= NULL
;
3833 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
3838 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
3842 nSubItems
= MenuInfo
.MenuItemCount
;
3844 /* FIXME - ported from wine, does that work (0xff)? */
3845 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
3846 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
3848 return (UINT
)-1; /* Invalid submenu */
3851 /* FIXME - ported from wine, does that work? */
3852 return (mii
.fType
| mii
.fState
);
3872 mii
.dwTypeData
= lpString
;
3873 mii
.fMask
= MIIM_STRING
;
3874 mii
.fType
= MF_STRING
;
3875 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3876 mii
.cch
= nMaxCount
;
3878 if(!(GetMenuItemInfoA( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&mii
)))
3898 miiW
.dwTypeData
= lpString
;
3899 miiW
.fMask
= MIIM_STRING
;
3900 miiW
.cbSize
= sizeof(MENUITEMINFOW
);
3901 miiW
.cch
= nMaxCount
;
3903 if(!(GetMenuItemInfoW( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&miiW
)))
3921 mi
.cbSize
= sizeof(MENUITEMINFOW
);
3922 mi
.fMask
= MIIM_SUBMENU
;
3924 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
3926 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
3943 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
3945 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
3960 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
3973 UINT_PTR uIDNewItem
,
3977 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3978 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
3980 mii
.fState
= MFS_ENABLED
;
3982 if(uFlags
& MF_BITMAP
)
3984 mii
.fType
|= MFT_BITMAP
;
3985 mii
.fMask
|= MIIM_BITMAP
;
3986 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
3988 else if(uFlags
& MF_OWNERDRAW
)
3990 mii
.fType
|= MFT_OWNERDRAW
;
3991 mii
.fMask
|= MIIM_DATA
;
3992 mii
.dwItemData
= (DWORD
) lpNewItem
;
3996 mii
.fMask
|= MIIM_TYPE
;
3997 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
3998 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
4001 if(uFlags
& MF_RIGHTJUSTIFY
)
4003 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4005 if(uFlags
& MF_MENUBREAK
)
4007 mii
.fType
|= MFT_MENUBREAK
;
4009 if(uFlags
& MF_MENUBARBREAK
)
4011 mii
.fType
|= MFT_MENUBARBREAK
;
4013 if(uFlags
& MF_DISABLED
)
4015 mii
.fState
|= MFS_DISABLED
;
4017 if(uFlags
& MF_GRAYED
)
4019 mii
.fState
|= MFS_GRAYED
;
4022 if(uFlags
& MF_POPUP
)
4024 mii
.fType
|= MF_POPUP
;
4025 mii
.fMask
|= MIIM_SUBMENU
;
4026 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4030 mii
.fMask
|= MIIM_ID
;
4031 mii
.wID
= (UINT
)uIDNewItem
;
4033 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4046 LPCMENUITEMINFOA lpmii
)
4049 UNICODE_STRING MenuText
;
4051 BOOL CleanHeap
= FALSE
;
4054 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4055 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4057 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4059 /* copy the text string */
4060 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4061 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4063 Status
= RtlCreateUnicodeStringFromAsciiz(&MenuText
, (LPSTR
)mi
.dwTypeData
);
4064 if (!NT_SUCCESS (Status
))
4066 SetLastError (RtlNtStatusToDosError(Status
));
4069 mi
.dwTypeData
= MenuText
.Buffer
;
4070 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4074 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4076 if ( CleanHeap
) RtlFreeUnicodeString ( &MenuText
);
4091 LPCMENUITEMINFOW lpmii
)
4094 UNICODE_STRING MenuText
;
4096 mi
.hbmpItem
= (HBITMAP
)0;
4098 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4099 if a bad user passes bad data, we crash his process instead of the
4102 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4103 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4105 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
4107 /* copy the text string */
4108 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4109 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) &&
4110 mi
.dwTypeData
!= NULL
)
4112 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4113 mi
.dwTypeData
= MenuText
.Buffer
;
4114 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4117 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4132 UINT_PTR uIDNewItem
,
4136 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4137 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4139 mii
.fState
= MFS_ENABLED
;
4141 if(uFlags
& MF_BITMAP
)
4143 mii
.fType
|= MFT_BITMAP
;
4144 mii
.fMask
|= MIIM_BITMAP
;
4145 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4147 else if(uFlags
& MF_OWNERDRAW
)
4149 mii
.fType
|= MFT_OWNERDRAW
;
4150 mii
.fMask
|= MIIM_DATA
;
4151 mii
.dwItemData
= (DWORD
) lpNewItem
;
4155 mii
.fMask
|= MIIM_TYPE
;
4156 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4157 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4160 if(uFlags
& MF_RIGHTJUSTIFY
)
4162 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4164 if(uFlags
& MF_MENUBREAK
)
4166 mii
.fType
|= MFT_MENUBREAK
;
4168 if(uFlags
& MF_MENUBARBREAK
)
4170 mii
.fType
|= MFT_MENUBARBREAK
;
4172 if(uFlags
& MF_DISABLED
)
4174 mii
.fState
|= MFS_DISABLED
;
4176 if(uFlags
& MF_GRAYED
)
4178 mii
.fState
|= MFS_GRAYED
;
4181 if(uFlags
& MF_POPUP
)
4183 mii
.fType
|= MF_POPUP
;
4184 mii
.fMask
|= MIIM_SUBMENU
;
4185 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4189 mii
.fMask
|= MIIM_ID
;
4190 mii
.wID
= (UINT
)uIDNewItem
;
4192 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4204 ROSMENUINFO MenuInfo
;
4206 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4214 LoadMenuA(HINSTANCE hInstance
,
4217 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4218 if (Resource
== NULL
)
4222 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4230 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4232 return(LoadMenuIndirectW(lpMenuTemplate
));
4240 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4243 WORD version
, offset
;
4244 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4246 version
= GET_WORD(p
);
4251 case 0: /* standard format is version of 0 */
4252 offset
= GET_WORD(p
);
4253 p
+= sizeof(WORD
) + offset
;
4254 if (!(hMenu
= CreateMenu())) return 0;
4255 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4261 case 1: /* extended format is version of 1 */
4262 offset
= GET_WORD(p
);
4263 p
+= sizeof(WORD
) + offset
;
4264 if (!(hMenu
= CreateMenu())) return 0;
4265 if (!MENUEX_ParseResource(p
, hMenu
))
4267 DestroyMenu( hMenu
);
4272 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4282 LoadMenuW(HINSTANCE hInstance
,
4285 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4286 if (Resource
== NULL
)
4290 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4304 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4317 UINT_PTR uIDNewItem
,
4321 memset( &mii
, 0, sizeof(mii
) );
4322 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4323 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4325 mii
.fState
= MFS_ENABLED
;
4329 if(!GetMenuItemInfoA( hMnu
,
4331 (BOOL
)(MF_BYPOSITION
& uFlags
),
4332 &mii
)) return FALSE
;
4334 if(uFlags
& MF_BITMAP
)
4336 mii
.fType
|= MFT_BITMAP
;
4337 mii
.fMask
|= MIIM_BITMAP
;
4338 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4340 else if(uFlags
& MF_OWNERDRAW
)
4342 mii
.fType
|= MFT_OWNERDRAW
;
4343 mii
.fMask
|= MIIM_DATA
;
4344 mii
.dwItemData
= (DWORD
) lpNewItem
;
4346 else /* Default action MF_STRING. */
4348 if(mii
.dwTypeData
!= NULL
)
4350 HeapFree(GetProcessHeap(),0, mii
.dwTypeData
);
4352 /* Item beginning with a backspace is a help item */
4353 if (*lpNewItem
== '\b')
4355 mii
.fType
|= MF_HELP
;
4358 mii
.fMask
|= MIIM_TYPE
;
4359 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
4360 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
4363 if(uFlags
& MF_RIGHTJUSTIFY
)
4365 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4367 if(uFlags
& MF_MENUBREAK
)
4369 mii
.fType
|= MFT_MENUBREAK
;
4371 if(uFlags
& MF_MENUBARBREAK
)
4373 mii
.fType
|= MFT_MENUBARBREAK
;
4375 if(uFlags
& MF_DISABLED
)
4377 mii
.fState
|= MFS_DISABLED
;
4379 if(uFlags
& MF_GRAYED
)
4381 mii
.fState
|= MFS_GRAYED
;
4384 if ((mii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (mii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4385 NtUserDestroyMenu( mii
.hSubMenu
); /* ModifyMenu() spec */
4387 if(uFlags
& MF_POPUP
)
4389 mii
.fType
|= MF_POPUP
;
4390 mii
.fMask
|= MIIM_SUBMENU
;
4391 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4395 mii
.fMask
|= MIIM_ID
;
4396 mii
.wID
= (UINT
)uIDNewItem
;
4399 return SetMenuItemInfoA( hMnu
,
4401 (BOOL
)(MF_BYPOSITION
& uFlags
),
4415 UINT_PTR uIDNewItem
,
4419 memset ( &mii
, 0, sizeof(mii
) );
4420 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4421 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4422 mii
.fState
= MFS_ENABLED
;
4426 if(!NtUserMenuItemInfo( hMnu
,
4428 (BOOL
)(MF_BYPOSITION
& uFlags
),
4429 (PROSMENUITEMINFO
) &mii
,
4430 FALSE
)) return FALSE
;
4432 if(uFlags
& MF_BITMAP
)
4434 mii
.fType
|= MFT_BITMAP
;
4435 mii
.fMask
|= MIIM_BITMAP
;
4436 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4438 else if(uFlags
& MF_OWNERDRAW
)
4440 mii
.fType
|= MFT_OWNERDRAW
;
4441 mii
.fMask
|= MIIM_DATA
;
4442 mii
.dwItemData
= (DWORD
) lpNewItem
;
4446 /*if(mii.dwTypeData != NULL)
4448 HeapFree(GetProcessHeap(),0, mii.dwTypeData);
4450 if (*lpNewItem
== '\b')
4452 mii
.fType
|= MF_HELP
;
4455 mii
.fMask
|= MIIM_TYPE
;
4456 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4457 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4460 if(uFlags
& MF_RIGHTJUSTIFY
)
4462 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4464 if(uFlags
& MF_MENUBREAK
)
4466 mii
.fType
|= MFT_MENUBREAK
;
4468 if(uFlags
& MF_MENUBARBREAK
)
4470 mii
.fType
|= MFT_MENUBARBREAK
;
4472 if(uFlags
& MF_DISABLED
)
4474 mii
.fState
|= MFS_DISABLED
;
4476 if(uFlags
& MF_GRAYED
)
4478 mii
.fState
|= MFS_GRAYED
;
4481 if ((mii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (mii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4482 NtUserDestroyMenu( mii
.hSubMenu
);
4484 if(uFlags
& MF_POPUP
)
4486 mii
.fType
|= MF_POPUP
;
4487 mii
.fMask
|= MIIM_SUBMENU
;
4488 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4492 mii
.fMask
|= MIIM_ID
;
4493 mii
.wID
= (UINT
)uIDNewItem
;
4496 return SetMenuItemInfoW( hMnu
,
4498 (BOOL
)(MF_BYPOSITION
& uFlags
),
4513 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
4524 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4538 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
4553 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4556 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4557 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4570 HBITMAP hBitmapUnchecked
,
4571 HBITMAP hBitmapChecked
)
4573 ROSMENUITEMINFO uItem
;
4575 if(!(NtUserMenuItemInfo(hMenu
, uPosition
,
4576 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, FALSE
))) return FALSE
;
4578 if (!hBitmapChecked
&& !hBitmapUnchecked
)
4580 uItem
.fState
&= ~MF_USECHECKBITMAPS
;
4582 else /* Install new bitmaps */
4584 uItem
.hbmpChecked
= hBitmapChecked
;
4585 uItem
.hbmpUnchecked
= hBitmapUnchecked
;
4586 uItem
.fState
|= MF_USECHECKBITMAPS
;
4588 return NtUserMenuItemInfo(hMenu
, uPosition
,
4589 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, TRUE
);
4602 LPCMENUITEMINFOA lpmii
)
4604 MENUITEMINFOW MenuItemInfoW
;
4605 UNICODE_STRING UnicodeString
;
4608 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4610 if ((MenuItemInfoW
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4611 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
) &&
4612 MenuItemInfoW
.dwTypeData
!= NULL
)
4614 RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4615 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4616 MenuItemInfoW
.dwTypeData
= UnicodeString
.Buffer
;
4617 MenuItemInfoW
.cch
= UnicodeString
.Length
/ sizeof(WCHAR
);
4621 UnicodeString
.Buffer
= NULL
;
4624 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4625 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4627 if (UnicodeString
.Buffer
!= NULL
)
4629 RtlFreeUnicodeString(&UnicodeString
);
4645 LPCMENUITEMINFOW lpmii
)
4647 MENUITEMINFOW MenuItemInfoW
;
4649 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4650 if (0 != (MenuItemInfoW
.fMask
& MIIM_STRING
))
4652 MenuItemInfoW
.cch
= wcslen(MenuItemInfoW
.dwTypeData
);
4655 return NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4656 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4670 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4675 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4678 return NtUserSetSystemMenu(hwnd
, hMenu
);
4698 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
4700 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4701 if (0 == (Flags
& TPM_NONOTIFY
))
4703 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
4706 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
4708 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
4710 MenuExitTracking(Wnd
);
4729 /* Not fully implemented */
4730 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
4731 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
4740 SetMenuContextHelpId(HMENU hmenu
,
4741 DWORD dwContextHelpId
)
4743 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
4752 GetMenuContextHelpId(HMENU hmenu
)
4755 mi
.cbSize
= sizeof(ROSMENUINFO
);
4756 mi
.fMask
= MIM_HELPID
;
4758 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
4760 return mi
.dwContextHelpID
;
4805 LPCWSTR lpszNewItem
,
4810 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4811 for MF_DELETE. We should check the parameters for all others
4812 MF_* actions also (anybody got a doc on ChangeMenu?).
4815 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4818 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4821 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4824 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4827 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4828 flags
&~ MF_REMOVE
);
4830 default : /* MF_INSERT */
4831 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
4848 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4849 for MF_DELETE. We should check the parameters for all others
4850 MF_* actions also (anybody got a doc on ChangeMenu?).
4853 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4856 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4859 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4862 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4865 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4866 flags
&~ MF_REMOVE
);
4868 default : /* MF_INSERT */
4869 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);