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 ******************************************************************/
42 #include <user32/callback.h>
43 #include "user32/regcontrol.h"
44 #include "../controls/controls.h"
49 /* internal popup menu window messages */
50 #define MM_SETMENUHANDLE (WM_USER + 0)
51 #define MM_GETMENUHANDLE (WM_USER + 1)
53 /* Internal MenuTrackMenu() flags */
54 #define TPM_INTERNAL 0xF0000000
55 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
56 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
57 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
59 /* TYPES *********************************************************************/
61 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
63 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
67 #define IS_SYSTEM_MENU(MenuInfo) \
68 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
70 #define IS_SYSTEM_POPUP(MenuInfo) \
71 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
73 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
75 #define MENU_BAR_ITEMS_SPACE (12)
76 #define SEPARATOR_HEIGHT (5)
77 #define MENU_TAB_SPACE (8)
83 #define MF_END (0x0080)
87 #define MIIM_STRING (0x00000040)
90 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
91 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
92 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
93 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
95 /* internal flags for menu tracking */
97 #define TF_ENDMENU 0x0001
98 #define TF_SUSPENDPOPUP 0x0002
99 #define TF_SKIPREMOVE 0x0004
104 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
105 HMENU TopMenu
; /* initial menu */
106 HWND OwnerWnd
; /* where notifications are sent */
110 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
112 /*********************************************************************
113 * PopupMenu class descriptor
115 const struct builtin_class_descr POPUPMENU_builtin_class
=
117 POPUPMENU_CLASS_ATOMW
, /* name */
118 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
119 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
120 (WNDPROC
) NULL
, /* FIXME - procA */
121 sizeof(MENUINFO
*), /* extra */
122 (LPCWSTR
) IDC_ARROW
, /* cursor */
123 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
127 /* INTERNAL FUNCTIONS ********************************************************/
129 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
130 * Of course I didnt copy the ASM code because we want this to be portable
131 * and it needs to go away.
135 #define GET_WORD(ptr) (*(WORD *)(ptr))
138 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
141 HFONT hMenuFont
= NULL
;
142 HFONT hMenuFontBold
= NULL
;
144 /* Flag set by EndMenu() to force an exit from menu tracking */
145 static BOOL fEndMenu
= FALSE
;
147 /* Use global popup window because there's no way 2 menus can
148 * be tracked at the same time. */
149 static HWND TopPopup
;
151 /* Dimension of the menu bitmaps */
152 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
154 static HBITMAP StdMnArrow
= NULL
;
155 static HBITMAP BmpSysMenu
= NULL
;
157 /***********************************************************************
160 * Get full information about menu
163 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
165 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
166 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
168 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
171 /***********************************************************************
174 * Set full information about menu
177 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
179 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
180 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
182 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
185 /***********************************************************************
186 * MenuInitRosMenuItemInfo
188 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
191 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
193 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
194 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
197 /***********************************************************************
198 * MenuGetRosMenuItemInfo
200 * Get full information about a menu item
203 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
205 if (ItemInfo
->dwTypeData
!= NULL
)
207 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
210 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
211 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
212 ItemInfo
->dwTypeData
= NULL
;
214 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
220 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
)
223 ItemInfo
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0,
224 ItemInfo
->cch
* sizeof(WCHAR
));
225 if (NULL
== ItemInfo
->dwTypeData
)
230 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
240 /***********************************************************************
241 * MenuSetRosMenuItemInfo
243 * Set full information about a menu item
246 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
250 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
&&
251 ItemInfo
->dwTypeData
!= NULL
)
253 ItemInfo
->cch
= wcslen(ItemInfo
->dwTypeData
);
255 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
256 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
259 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
264 /***********************************************************************
265 * MenuCleanupRosMenuItemInfo
267 * Cleanup after use of MenuGet/SetRosMenuItemInfo
270 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
272 if (ItemInfo
->dwTypeData
!= NULL
)
274 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
278 /***********************************************************************
279 * MenuGetAllRosMenuItemInfo
281 * Get full information about all menu items
284 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
288 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
293 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
294 if (NULL
== *ItemInfo
)
299 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
302 /***********************************************************************
303 * MenuCleanupAllRosMenuItemInfo
305 * Cleanup after use of MenuGetAllRosMenuItemInfo
308 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
310 HeapFree(GetProcessHeap(), 0, ItemInfo
);
314 /***********************************************************************
317 * Load the arrow bitmap. We can't do this from MenuInit since user32
318 * can also be used (and thus initialized) from text-mode.
321 MenuLoadBitmaps(VOID
)
323 /* Load menu bitmaps */
324 if (NULL
== StdMnArrow
)
326 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
328 if (NULL
!= StdMnArrow
)
331 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
332 ArrowBitmapWidth
= bm
.bmWidth
;
333 ArrowBitmapHeight
= bm
.bmHeight
;
337 /* Load system buttons bitmaps */
338 if (NULL
== BmpSysMenu
)
340 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
344 /***********************************************************************
345 * MenuGetBitmapItemSize
347 * Get the size of a bitmap item.
350 MenuGetBitmapItemSize(UINT Id
, DWORD Data
, SIZE
*Size
)
353 HBITMAP Bmp
= (HBITMAP
) Id
;
355 Size
->cx
= Size
->cy
= 0;
357 /* check if there is a magic menu item associated with this item */
358 if (0 != Id
&& IS_MAGIC_ITEM(Id
))
360 switch((INT_PTR
) LOWORD(Id
))
362 case (INT_PTR
) HBMMENU_SYSTEM
:
365 Bmp
= (HBITMAP
) Data
;
369 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
370 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
371 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
372 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
373 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
374 /* FIXME: Why we need to subtract these magic values? */
375 Size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
376 Size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
378 case (INT_PTR
) HBMMENU_CALLBACK
:
379 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
380 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
381 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
382 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
384 DPRINT("Magic menu bitmap not implemented\n");
389 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
391 Size
->cx
= Bm
.bmWidth
;
392 Size
->cy
= Bm
.bmHeight
;
396 /***********************************************************************
399 * Draw a bitmap item.
402 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
, BOOL MenuBar
)
407 HBITMAP Bmp
= (HBITMAP
) Item
->hbmpItem
;
408 int w
= Rect
->right
- Rect
->left
;
409 int h
= Rect
->bottom
- Rect
->top
;
413 /* Check if there is a magic menu item associated with this item */
414 if (IS_MAGIC_ITEM(Item
->hbmpItem
))
420 switch ((int) Item
->hbmpItem
)
422 case (INT_PTR
) HBMMENU_SYSTEM
:
423 if (NULL
!= Item
->hbmpItem
)
425 Bmp
= Item
->hbmpItem
;
426 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
434 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
438 /* only use right half of the bitmap */
439 BmpXoffset
= Bm
.bmWidth
/ 2;
440 Bm
.bmWidth
-= BmpXoffset
;
443 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
444 Flags
= DFCS_CAPTIONRESTORE
;
446 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
448 Flags
= DFCS_CAPTIONMIN
;
450 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
452 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
454 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
455 Flags
= DFCS_CAPTIONCLOSE
;
457 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
458 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
460 case (INT_PTR
) HBMMENU_CALLBACK
:
461 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
462 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
463 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
464 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
466 DPRINT("Magic menu bitmap not implemented\n");
469 InflateRect(&r
, -1, -1);
470 if (0 != (Item
->fState
& MF_HILITE
))
472 Flags
|= DFCS_PUSHED
;
474 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
478 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
484 DcMem
= CreateCompatibleDC(Dc
);
485 SelectObject(DcMem
, Bmp
);
487 /* handle fontsize > bitmap_height */
488 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
490 Rop
= (0 != (Item
->fState
& MF_HILITE
) && ! IS_MAGIC_ITEM(Item
->hbmpItem
)) ? NOTSRCCOPY
: SRCCOPY
;
491 if (0 != (Item
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(Item
->fType
))
493 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
495 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
499 /***********************************************************************
502 * Draw a single menu item.
505 MenuDrawMenuItem(HWND Wnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
506 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
511 if (0 != (Item
->fType
& MF_SYSMENU
))
515 UserGetInsideRectNC(Wnd
, &Rect
);
516 UserDrawSysMenuButton(Wnd
, Dc
, &Rect
,
517 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
525 if (0 != (Item
->fState
& MF_HILITE
))
529 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
530 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
534 if (0 != (Item
->fState
& MF_GRAYED
))
536 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
540 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
542 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
547 if (0 != (Item
->fState
& MF_GRAYED
))
549 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
553 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
555 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
558 if (0 != (Item
->fType
& MF_OWNERDRAW
))
561 ** Experimentation under Windows reveals that an owner-drawn
562 ** menu is given the rectangle which includes the space it requested
563 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
564 ** and a popup-menu arrow. This is the value of lpitem->rect.
565 ** Windows will leave all drawing to the application except for
566 ** the popup-menu arrow. Windows always draws that itself, after
567 ** the menu owner has finished drawing.
571 dis
.CtlType
= ODT_MENU
;
573 dis
.itemID
= Item
->wID
;
574 dis
.itemData
= (DWORD
)Item
->dwItemData
;
576 if (0 != (Item
->fState
& MF_CHECKED
))
578 dis
.itemState
|= ODS_CHECKED
;
580 if (0 != (Item
->fState
& MF_GRAYED
))
582 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
584 if (0 != (Item
->fState
& MF_HILITE
))
586 dis
.itemState
|= ODS_SELECTED
;
588 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
589 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
591 dis
.rcItem
= Item
->Rect
;
592 DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
593 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
594 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
595 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
597 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
598 /* Fall through to draw popup-menu arrow */
601 DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
602 Item
->Rect
.right
, Item
->Rect
.bottom
);
604 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
611 if (0 == (Item
->fType
& MF_OWNERDRAW
))
613 if (Item
->fState
& MF_HILITE
)
617 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
621 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
626 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENU
));
630 SetBkMode(Dc
, TRANSPARENT
);
632 if (0 == (Item
->fType
& MF_OWNERDRAW
))
634 /* vertical separator */
635 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
639 rc
.bottom
= Height
- 3;
640 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
643 /* horizontal separator */
644 if (0 != (Item
->fType
& MF_SEPARATOR
))
649 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
650 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
657 /* helper lines for debugging */
658 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
659 SelectObject(Dc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
));
660 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
661 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
666 INT y
= Rect
.top
+ Rect
.bottom
;
667 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
668 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
670 if (0 == (Item
->fType
& MF_OWNERDRAW
))
672 /* Draw the check mark
675 * Custom checkmark bitmaps are monochrome but not always 1bpp.
677 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
678 if (NULL
!= bm
) /* we have a custom bitmap */
680 HDC DcMem
= CreateCompatibleDC(Dc
);
681 SelectObject(DcMem
, bm
);
682 BitBlt(Dc
, Rect
.left
, (y
- CheckBitmapHeight
) / 2,
683 CheckBitmapWidth
, CheckBitmapHeight
,
684 DcMem
, 0, 0, SRCCOPY
);
687 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
690 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
691 HDC DcMem
= CreateCompatibleDC(Dc
);
692 SelectObject(DcMem
, bm
);
693 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
694 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
695 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
696 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
697 BitBlt(Dc
, Rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
698 DcMem
, 0, 0, SRCCOPY
);
704 /* Draw the popup-menu arrow */
705 if (0 != (Item
->fType
& MF_POPUP
))
707 HDC DcMem
= CreateCompatibleDC(Dc
);
710 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
711 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
712 (y
- ArrowBitmapHeight
) / 2,
713 ArrowBitmapWidth
, ArrowBitmapHeight
,
714 DcMem
, 0, 0, SRCCOPY
);
715 SelectObject(DcMem
, OrigBitmap
);
719 Rect
.left
+= CheckBitmapWidth
;
720 Rect
.right
-= ArrowBitmapWidth
;
723 /* Done for owner-drawn */
724 if (0 != (Item
->fType
& MF_OWNERDRAW
))
729 /* Draw the item text or bitmap */
730 if (IS_BITMAP_ITEM(Item
->fType
))
732 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuBar
);
735 /* No bitmap - process text if present */
736 else if (IS_STRING_ITEM(Item
->fType
))
739 HFONT FontOld
= NULL
;
741 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
742 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
744 if (0 != (Item
->fState
& MFS_DEFAULT
))
746 FontOld
= SelectObject(Dc
, hMenuFontBold
);
751 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
752 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
755 Text
= (PWCHAR
) Item
->dwTypeData
;
756 for (i
= 0; L
'\0' != Text
[i
]; i
++)
758 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
764 if (0 != (Item
->fState
& MF_GRAYED
))
766 if (0 == (Item
->fState
& MF_HILITE
))
768 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
769 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
770 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
771 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
773 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
776 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
778 /* paint the shortcut text */
779 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
781 if (L
'\t' == Text
[i
])
783 Rect
.left
= Item
->XTab
;
784 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
788 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
791 if (0 != (Item
->fState
& MF_GRAYED
))
793 if (0 == (Item
->fState
& MF_HILITE
))
795 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
796 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
797 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
798 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
800 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
802 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
807 SelectObject(Dc
, FontOld
);
812 /***********************************************************************
815 * Paint a popup menu.
818 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
820 HBRUSH PrevBrush
= NULL
;
823 ROSMENUINFO MenuInfo
;
824 ROSMENUITEMINFO ItemInfo
;
827 DPRINT("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
829 GetClientRect(Wnd
, &Rect
);
831 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
832 && NULL
!= SelectObject(Dc
, hMenuFont
))
834 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
836 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
839 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
841 /* draw menu items */
843 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
845 MenuInitRosMenuItemInfo(&ItemInfo
);
847 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
849 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
851 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
852 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
856 MenuCleanupRosMenuItemInfo(&ItemInfo
);
861 SelectObject(Dc
, PrevBrush
);
866 static LRESULT WINAPI
867 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
869 DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
875 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
876 SetWindowLongW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
880 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
881 return MA_NOACTIVATE
;
886 BeginPaint(Wnd
, &ps
);
887 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongW(Wnd
, 0));
896 /* zero out global pointer in case resident popup window was destroyed. */
906 if (0 == GetWindowLongW(Wnd
, 0))
908 OutputDebugStringA("no menu to display\n");
913 SetWindowLongW(Wnd
, 0, 0);
917 case MM_SETMENUHANDLE
:
918 SetWindowLongW(Wnd
, 0, wParam
);
921 case MM_GETMENUHANDLE
:
922 return GetWindowLongW(Wnd
, 0);
925 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
931 /**********************************************************************
932 * MENUEX_ParseResource
934 * Parse an extended menu resource and add items to the menu.
935 * Return a pointer to the end of the resource.
937 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
939 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
947 mii
.cbSize
= sizeof(mii
);
948 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
949 mii
.fType
= GET_DWORD(res
);
950 res
+= sizeof(DWORD
);
951 mii
.fState
= GET_DWORD(res
);
952 res
+= sizeof(DWORD
);
953 mii
.wID
= GET_DWORD(res
);
954 res
+= sizeof(DWORD
);
955 resinfo
= GET_WORD(res
);
957 /* Align the text on a word boundary. */
958 res
+= (~((int)res
- 1)) & 1;
959 mii
.dwTypeData
= (LPWSTR
) res
;
960 res
+= (1 + wcslen(mii
.dwTypeData
)) * sizeof(WCHAR
);
961 /* Align the following fields on a dword boundary. */
962 res
+= (~((int)res
- 1)) & 3;
964 if (resinfo
& 1) /* Pop-up? */
966 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
967 res
+= sizeof(DWORD
);
968 mii
.hSubMenu
= CreatePopupMenu();
971 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
973 DestroyMenu(mii
.hSubMenu
);
976 mii
.fMask
|= MIIM_SUBMENU
;
977 mii
.fType
|= MF_POPUP
;
979 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
981 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
983 mii
.fType
|= MF_SEPARATOR
;
985 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
987 while (!(resinfo
& MF_END
));
992 /**********************************************************************
995 * Parse a standard menu resource and add items to the menu.
996 * Return a pointer to the end of the resource.
998 * NOTE: flags is equivalent to the mtOption field
1000 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1009 flags
= GET_WORD(res
);
1011 /* remove MF_END flag before passing it to AppendMenu()! */
1012 end
= (flags
& MF_END
);
1013 if(end
) flags
^= MF_END
;
1015 res
+= sizeof(WORD
);
1016 if(!(flags
& MF_POPUP
))
1019 res
+= sizeof(WORD
);
1023 res
+= strlen(str
) + 1;
1025 res
+= (wcslen((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1026 if (flags
& MF_POPUP
)
1028 hSubMenu
= CreatePopupMenu();
1029 if(!hSubMenu
) return NULL
;
1030 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1033 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1035 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1037 else /* Not a popup */
1040 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1042 AppendMenuW(hMenu
, flags
, id
,
1043 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1052 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1056 hUser32
= GetModuleHandleW(L
"USER32");
1057 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
1058 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1065 NONCLIENTMETRICSW ncm
;
1067 /* get the menu font */
1068 if(!hMenuFont
|| !hMenuFontBold
)
1070 ncm
.cbSize
= sizeof(ncm
);
1071 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1073 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1077 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1078 if(hMenuFont
== NULL
)
1080 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1084 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1085 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1086 if(hMenuFontBold
== NULL
)
1088 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1097 /***********************************************************************
1100 * Calculate the size of the menu item and store it in ItemInfo->rect.
1102 static void FASTCALL
1103 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, HWND WndOwner
,
1104 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1107 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1109 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1111 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1113 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1116 ** Experimentation under Windows reveals that an owner-drawn
1117 ** menu is expected to return the size of the content part of
1118 ** the menu item, not including the checkmark nor the submenu
1119 ** arrow. Windows adds those values itself and returns the
1120 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1122 MEASUREITEMSTRUCT mis
;
1123 mis
.CtlType
= ODT_MENU
;
1125 mis
.itemID
= ItemInfo
->wID
;
1126 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1129 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1130 ItemInfo
->Rect
.right
+= mis
.itemWidth
;
1134 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1136 /* under at least win95 you seem to be given a standard
1137 height for the menu and the height value is ignored */
1139 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENU
) - 1;
1143 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1146 DPRINT("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1147 /* Fall through to get check/arrow width calculation. */
1150 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1152 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1158 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1159 if (0 != (ItemInfo
->fType
& MF_POPUP
))
1161 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1165 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1170 if (IS_BITMAP_ITEM(ItemInfo
->fType
))
1174 MenuGetBitmapItemSize((int) ItemInfo
->hbmpItem
, (DWORD
) ItemInfo
->hbmpItem
, &Size
);
1175 ItemInfo
->Rect
.right
+= Size
.cx
;
1176 ItemInfo
->Rect
.bottom
+= Size
.cy
;
1178 /* Leave space for the sunken border */
1179 ItemInfo
->Rect
.right
+= 2;
1180 ItemInfo
->Rect
.bottom
+= 2;
1182 /* Special case: Minimize button doesn't have a space behind it. */
1183 if (ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1184 ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1185 ItemInfo
->Rect
.right
-= 1;
1188 /* it must be a text item - unless it's the system menu */
1189 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && IS_STRING_ITEM(ItemInfo
->fType
))
1193 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1194 wcslen((LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1196 ItemInfo
->Rect
.right
+= Size
.cx
;
1197 ItemInfo
->Rect
.bottom
+= max(Size
.cy
, GetSystemMetrics(SM_CYMENU
) - 1);
1202 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1204 else if ((p
= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\t' )) != NULL
)
1206 /* Item contains a tab (only meaningful in popup menus) */
1207 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1208 (int)(p
- (LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1209 ItemInfo
->XTab
= CheckBitmapWidth
+ MENU_TAB_SPACE
+ Size
.cx
;
1210 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1214 if (NULL
!= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\b'))
1216 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1218 ItemInfo
->XTab
= ItemInfo
->Rect
.right
- CheckBitmapWidth
1223 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1226 /***********************************************************************
1227 * MenuPopupMenuCalcSize
1229 * Calculate the size of a popup menu.
1231 static void FASTCALL
1232 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1234 ROSMENUITEMINFO ItemInfo
;
1237 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1239 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1240 if (0 == MenuInfo
->MenuItemCount
)
1242 MenuSetRosMenuInfo(MenuInfo
);
1247 SelectObject(Dc
, hMenuFont
);
1252 MenuInitRosMenuItemInfo(&ItemInfo
);
1253 while (Start
< MenuInfo
->MenuItemCount
)
1258 MaxTab
= MaxTabWidth
= 0;
1260 /* Parse items until column break or end of menu */
1261 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1263 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1265 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1266 MenuSetRosMenuInfo(MenuInfo
);
1270 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1275 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1276 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1278 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1279 MenuSetRosMenuInfo(MenuInfo
);
1283 if (0 != (ItemInfo
.fType
& MF_MENUBARBREAK
))
1287 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1288 OrgY
= ItemInfo
.Rect
.bottom
;
1289 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1291 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1292 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1296 /* Finish the column (set all items to the largest width found) */
1297 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1300 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1302 ItemInfo
.Rect
.right
= MaxX
;
1303 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1305 ItemInfo
.XTab
= MaxTab
;
1307 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1311 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1314 MenuInfo
->Width
= MaxX
;
1316 /* space for 3d border */
1317 MenuInfo
->Height
+= 2;
1318 MenuInfo
->Width
+= 2;
1320 ReleaseDC(NULL
, Dc
);
1321 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1322 MenuSetRosMenuInfo(MenuInfo
);
1325 /***********************************************************************
1326 * MenuMenuBarCalcSize
1328 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1329 * height is off by 1 pixel which causes lengthy window relocations when
1330 * active document window is maximized/restored.
1332 * Calculate the size of the menu bar.
1334 static void FASTCALL
1335 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1337 ROSMENUITEMINFO ItemInfo
;
1338 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1340 if (NULL
== Rect
|| NULL
== MenuInfo
)
1344 if (0 == MenuInfo
->MenuItemCount
)
1349 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1350 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1351 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1352 MenuInfo
->Height
= 0;
1353 MaxY
= Rect
->top
+ 1;
1356 MenuInitRosMenuItemInfo(&ItemInfo
);
1357 while (Start
< MenuInfo
->MenuItemCount
)
1359 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1361 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1367 /* Parse items until line break or end of menu */
1368 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1370 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1375 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1380 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1381 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1382 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1384 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1388 if (ItemInfo
.Rect
.right
> Rect
->right
)
1396 ItemInfo
.Rect
.right
= Rect
->right
;
1399 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1400 OrgX
= ItemInfo
.Rect
.right
;
1401 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1403 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1405 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1411 /* FIXME: Is this really needed? */
1413 /* Finish the line (set all items to the largest height found) */
1416 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1418 ItemInfo
.Rect
.bottom
= MaxY
;
1419 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1428 Rect
->bottom
= MaxY
;
1429 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1430 MenuSetRosMenuInfo(MenuInfo
);
1434 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1435 /* the last item (if several lines, only move the last line) */
1436 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1438 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1441 OrgY
= ItemInfo
.Rect
.top
;
1443 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1449 if (ItemInfo
.Rect
.top
!= OrgY
)
1451 break; /* Other line */
1453 if (OrgX
<= ItemInfo
.Rect
.right
)
1455 break; /* Too far right already */
1457 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1458 ItemInfo
.Rect
.right
= OrgX
;
1459 OrgX
= ItemInfo
.Rect
.left
;
1460 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1461 if (HelpPos
+ 1 <= i
&&
1462 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1464 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1470 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1473 /***********************************************************************
1474 * DrawMenuBarTemp (USER32.@)
1478 * called by W98SE desk.cpl Control Panel Applet
1480 * Not 100% sure about the param names, but close.
1485 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1487 ROSMENUINFO MenuInfo
;
1488 ROSMENUITEMINFO ItemInfo
;
1490 HFONT FontOld
= NULL
;
1494 Menu
= GetMenu(Wnd
);
1502 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1504 return GetSystemMetrics(SM_CYMENU
);
1507 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1509 FontOld
= SelectObject(DC
, Font
);
1511 if (0 == MenuInfo
.Height
)
1513 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1516 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1518 FillRect(DC
, Rect
, GetSysColorBrush(COLOR_MENU
));
1520 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1521 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1522 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1524 if (0 == MenuInfo
.MenuItemCount
)
1526 SelectObject(DC
, FontOld
);
1527 return GetSystemMetrics(SM_CYMENU
);
1530 MenuInitRosMenuItemInfo(&ItemInfo
);
1531 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1533 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1535 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1536 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1539 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1541 SelectObject(DC
, FontOld
);
1543 return MenuInfo
.Height
;
1547 /***********************************************************************
1550 * Paint a menu bar. Returns the height of the menu bar.
1551 * called from [windows/nonclient.c]
1553 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1555 ROSMENUINFO MenuInfo
;
1556 HFONT FontOld
= NULL
;
1557 HMENU Menu
= GetMenu(Wnd
);
1559 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1561 return GetSystemMetrics(SM_CYMENU
);
1566 FontOld
= SelectObject(DC
, hMenuFont
);
1568 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1570 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1572 if (NULL
!= FontOld
)
1574 SelectObject(DC
, FontOld
);
1576 return MenuInfo
.Height
;
1580 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1584 /***********************************************************************
1587 static BOOL FASTCALL
1588 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1590 DPRINT("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1594 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1595 if (0 == (Flags
& TPM_NONOTIFY
))
1597 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1600 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1602 if (0 == (Flags
& TPM_NONOTIFY
))
1604 ROSMENUINFO MenuInfo
;
1606 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1608 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 == MenuInfo
.Height
)
1610 /* app changed/recreated menu bar entries in WM_INITMENU
1611 Recalculate menu sizes else clicks will not work */
1612 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1613 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1622 /***********************************************************************
1625 * Display a popup menu.
1627 static BOOL FASTCALL
1628 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1629 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1631 ROSMENUINFO MenuInfo
;
1632 ROSMENUITEMINFO ItemInfo
;
1635 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1636 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1638 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1643 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1645 MenuInitRosMenuItemInfo(&ItemInfo
);
1646 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1648 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1649 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1651 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1652 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1655 /* store the owner for DrawItem */
1656 MenuInfo
.WndOwner
= WndOwner
;
1657 MenuSetRosMenuInfo(&MenuInfo
);
1659 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1661 /* adjust popup menu pos so that it fits within the desktop */
1663 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1664 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1666 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1670 X
-= Width
- XAnchor
;
1672 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1674 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1682 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1686 Y
-= Height
+ YAnchor
;
1688 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1690 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1699 /* NOTE: In Windows, top menu popup is not owned. */
1700 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1701 WS_POPUP
, X
, Y
, Width
, Height
,
1702 WndOwner
, 0, (HINSTANCE
) GetWindowLongW(WndOwner
, GWL_HINSTANCE
),
1703 (LPVOID
) MenuInfo
.Self
);
1704 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1708 if (NULL
== TopPopup
)
1710 TopPopup
= MenuInfo
.Wnd
;
1713 /* Display the window */
1714 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1715 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1716 UpdateWindow(MenuInfo
.Wnd
);
1721 /***********************************************************************
1724 * Find a Sub menu. Return the position of the submenu, and modifies
1725 * *hmenu in case it is found in another sub-menu.
1726 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1728 static UINT FASTCALL
1729 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1731 ROSMENUINFO MenuInfo
;
1732 ROSMENUITEMINFO ItemInfo
;
1737 if ((HMENU
) 0xffff == *Menu
1738 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1740 return NO_SELECTED_ITEM
;
1743 MenuInitRosMenuItemInfo(&ItemInfo
);
1744 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1746 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1748 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1749 return NO_SELECTED_ITEM
;
1751 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1755 if (ItemInfo
.hSubMenu
== SubTarget
)
1757 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1760 SubMenu
= ItemInfo
.hSubMenu
;
1761 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1762 if (NO_SELECTED_ITEM
!= Pos
)
1768 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1770 return NO_SELECTED_ITEM
;
1773 /***********************************************************************
1776 static void FASTCALL
1777 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1778 BOOL SendMenuSelect
, HMENU TopMenu
)
1781 ROSMENUITEMINFO ItemInfo
;
1782 ROSMENUINFO TopMenuInfo
;
1785 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1787 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1792 if (MenuInfo
->FocusedItem
== Index
)
1797 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1799 Dc
= GetDC(MenuInfo
->Wnd
);
1803 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1806 if (NULL
== TopPopup
)
1808 TopPopup
= MenuInfo
->Wnd
;
1811 SelectObject(Dc
, hMenuFont
);
1812 MenuInitRosMenuItemInfo(&ItemInfo
);
1814 /* Clear previous highlighted item */
1815 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1817 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1819 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1820 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1822 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1823 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1827 /* Highlight new item (if any) */
1828 MenuInfo
->FocusedItem
= Index
;
1829 MenuSetRosMenuInfo(MenuInfo
);
1830 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1832 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1834 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1836 ItemInfo
.fState
|= MF_HILITE
;
1837 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1838 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
1839 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1844 SendMessageW(WndOwner
, WM_MENUSELECT
,
1845 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
1846 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1847 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
1851 else if (SendMenuSelect
)
1853 if (NULL
!= TopMenu
)
1855 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
1856 if (NO_SELECTED_ITEM
!= Pos
)
1858 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
1859 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
1861 SendMessageW(WndOwner
, WM_MENUSELECT
,
1862 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1864 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1871 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1872 ReleaseDC(MenuInfo
->Wnd
, Dc
);
1875 /***********************************************************************
1878 * Moves currently selected item according to the Offset parameter.
1879 * If there is no selection then it should select the last item if
1880 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1882 static void FASTCALL
1883 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1886 ROSMENUITEMINFO ItemInfo
;
1889 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1891 /* Prevent looping */
1892 if (0 == MenuInfo
->MenuItemCount
|| 0 == Offset
)
1894 else if (Offset
< -1)
1896 else if (Offset
> 1)
1899 MenuInitRosMenuItemInfo(&ItemInfo
);
1901 OrigPos
= MenuInfo
->FocusedItem
;
1902 if (OrigPos
== NO_SELECTED_ITEM
) /* NO_SELECTED_ITEM is not -1 ! */
1909 i
= MenuInfo
->FocusedItem
;
1916 /* Clip and wrap around */
1919 i
= MenuInfo
->MenuItemCount
- 1;
1921 else if (i
>= MenuInfo
->MenuItemCount
)
1925 /* If this is a good candidate; */
1926 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1927 0 == (ItemInfo
.fType
& MF_SEPARATOR
) &&
1928 0 == (ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
1930 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1931 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1934 } while (i
!= OrigPos
);
1937 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1940 /***********************************************************************
1941 * MenuInitSysMenuPopup
1943 * Grey the appropriate items in System menu.
1946 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
1954 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1955 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1956 Gray
= 0 != (Style
& WS_MAXIMIZE
);
1957 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1958 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
1959 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1960 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
1961 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1962 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1963 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1964 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
1966 /* The menu item must keep its state if it's disabled */
1969 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
1972 /* Set default menu item */
1973 if(Style
& WS_MINIMIZE
)
1975 DefItem
= SC_RESTORE
;
1979 if(HitTest
== HTCAPTION
)
1981 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
1989 mii
.cbSize
= sizeof(MENUITEMINFOW
);
1990 mii
.fMask
= MIIM_STATE
;
1991 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
1992 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
1997 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
2000 /***********************************************************************
2003 * Display the sub-menu of the selected item of this menu.
2004 * Return the handle of the submenu, or menu if no submenu to display.
2006 static HMENU FASTCALL
2007 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2009 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2011 ROSMENUITEMINFO ItemInfo
;
2012 ROSMENUINFO SubMenuInfo
;
2016 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2018 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2020 return MenuInfo
->Self
;
2023 MenuInitRosMenuItemInfo(&ItemInfo
);
2024 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2026 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2027 return MenuInfo
->Self
;
2029 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2031 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2032 return MenuInfo
->Self
;
2035 /* message must be sent before using item,
2036 because nearly everything may be changed by the application ! */
2038 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2039 if (0 == (Flags
& TPM_NONOTIFY
))
2041 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2042 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2045 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2047 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2048 return MenuInfo
->Self
;
2050 Rect
= ItemInfo
.Rect
;
2052 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2053 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2055 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2057 Dc
= GetDC(MenuInfo
->Wnd
);
2061 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2064 SelectObject(Dc
, hMenuFont
);
2066 ItemInfo
.fState
|= MF_HILITE
;
2067 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2068 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2069 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2070 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2073 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2074 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2076 ItemInfo
.Rect
= Rect
;
2079 ItemInfo
.fState
|= MF_MOUSESELECT
;
2081 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2083 if (IS_SYSTEM_MENU(MenuInfo
))
2085 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2086 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2088 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2089 Rect
.top
= Rect
.bottom
;
2090 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2091 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2095 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2096 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2098 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2099 Rect
.top
+= ItemInfo
.Rect
.top
;
2100 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2101 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
;
2105 Rect
.left
+= ItemInfo
.Rect
.left
;
2106 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2107 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2108 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2112 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2113 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2114 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2116 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2119 Ret
= ItemInfo
.hSubMenu
;
2120 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2125 /***********************************************************************
2128 * Hide the sub-popup menus of this menu.
2130 static void FASTCALL
2131 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2133 ROSMENUINFO SubMenuInfo
;
2134 ROSMENUITEMINFO ItemInfo
;
2136 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2138 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2140 MenuInitRosMenuItemInfo(&ItemInfo
);
2141 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2142 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2143 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2145 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2148 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2149 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2150 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2152 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2153 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2154 DestroyWindow(SubMenuInfo
.Wnd
);
2155 SubMenuInfo
.Wnd
= NULL
;
2156 MenuSetRosMenuInfo(&SubMenuInfo
);
2161 /***********************************************************************
2162 * MenuSwitchTracking
2164 * Helper function for menu navigation routines.
2166 static void FASTCALL
2167 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2169 ROSMENUINFO TopMenuInfo
;
2171 DPRINT("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2173 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2174 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2175 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2177 /* both are top level menus (system and menu-bar) */
2178 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2179 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2180 Mt
->TopMenu
= PtMenuInfo
->Self
;
2184 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2187 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2190 /***********************************************************************
2191 * MenuExecFocusedItem
2193 * Execute a menu item (for instance when user pressed Enter).
2194 * Return the wID of the executed item. Otherwise, -1 indicating
2195 * that no menu item was executed;
2196 * Have to receive the flags for the TrackPopupMenu options to avoid
2197 * sending unwanted message.
2201 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2203 ROSMENUITEMINFO ItemInfo
;
2206 DPRINT("%p menu=%p\n", Mt
, MenuInfo
);
2208 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2213 MenuInitRosMenuItemInfo(&ItemInfo
);
2214 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2216 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2220 DPRINT("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2222 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2224 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2225 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2227 /* If TPM_RETURNCMD is set you return the id, but
2228 do not send a message to the owner */
2229 if (0 == (Flags
& TPM_RETURNCMD
))
2231 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2233 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2234 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2238 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2242 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2248 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2254 /***********************************************************************
2257 * Return TRUE if we can go on with menu tracking.
2259 static BOOL FASTCALL
2260 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2263 ROSMENUINFO MenuInfo
;
2264 ROSMENUITEMINFO Item
;
2266 DPRINT("%x PtMenu=%p\n", Mt
, PtMenu
);
2270 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2274 if (IS_SYSTEM_MENU(&MenuInfo
))
2280 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2282 MenuInitRosMenuItemInfo(&Item
);
2283 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2285 MenuCleanupRosMenuItemInfo(&Item
);
2289 if (!(Item
.fType
& MF_SEPARATOR
) &&
2290 !(Item
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2292 if (MenuInfo
.FocusedItem
!= Index
)
2294 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2297 /* If the popup menu is not already "popped" */
2298 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2300 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2304 MenuCleanupRosMenuItemInfo(&Item
);
2309 /* else the click was on the menu bar, finish the tracking */
2314 /***********************************************************************
2317 * Return the value of MenuExecFocusedItem if
2318 * the selected item was not a popup. Else open the popup.
2319 * A -1 return value indicates that we go on with menu tracking.
2323 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2326 ROSMENUINFO MenuInfo
;
2327 ROSMENUITEMINFO ItemInfo
;
2329 DPRINT("%p hmenu=%x\n", Mt
, PtMenu
);
2334 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2339 if (! IS_SYSTEM_MENU(&MenuInfo
))
2341 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2343 MenuInitRosMenuItemInfo(&ItemInfo
);
2344 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2345 MenuInfo
.FocusedItem
== Id
)
2347 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2349 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2350 return MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2352 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2354 /* If we are dealing with the top-level menu */
2355 /* and this is a click on an already "popped" item: */
2356 /* Stop the menu tracking and close the opened submenus */
2357 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2359 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2363 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2364 MenuInfo
.TimeToHide
= TRUE
;
2365 MenuSetRosMenuInfo(&MenuInfo
);
2371 /***********************************************************************
2374 * Walks menu chain trying to find a menu pt maps to.
2376 static HMENU FASTCALL
2377 MenuPtMenu(HMENU Menu
, POINT Pt
)
2379 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2380 ROSMENUINFO MenuInfo
;
2381 ROSMENUITEMINFO ItemInfo
;
2385 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2390 /* try subpopup first (if any) */
2391 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2393 MenuInitRosMenuItemInfo(&ItemInfo
);
2394 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2395 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2396 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2398 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2401 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2405 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2408 /* check the current window (avoiding WM_HITTEST) */
2409 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2410 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2412 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2417 else if (HTSYSMENU
== Ht
)
2419 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2421 else if (HTMENU
== Ht
)
2423 Ret
= GetMenu(MenuInfo
.Wnd
);
2429 /***********************************************************************
2432 * Return TRUE if we can go on with menu tracking.
2434 static BOOL FASTCALL
2435 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2438 ROSMENUINFO MenuInfo
;
2439 ROSMENUITEMINFO ItemInfo
;
2443 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2447 if (IS_SYSTEM_MENU(&MenuInfo
))
2453 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2458 Index
= NO_SELECTED_ITEM
;
2461 if (NO_SELECTED_ITEM
== Index
)
2463 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2464 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2466 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2470 else if (MenuInfo
.FocusedItem
!= Index
)
2472 MenuInitRosMenuItemInfo(&ItemInfo
);
2473 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, Index
, &ItemInfo
) &&
2474 !(ItemInfo
.fType
& MF_SEPARATOR
) &&
2475 !(ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2477 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2478 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2480 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2486 /******************************************************************************
2488 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2490 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2493 PROSMENUITEMINFO MenuItems
;
2495 i
= MenuInfo
->FocusedItem
;
2496 if (NO_SELECTED_ITEM
== i
)
2501 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2503 return NO_SELECTED_ITEM
;
2506 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2508 if (0 != (MenuItems
[i
].fType
& MF_MENUBARBREAK
))
2514 return NO_SELECTED_ITEM
;
2517 /******************************************************************************
2519 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2521 static UINT FASTCALL
2522 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2525 PROSMENUITEMINFO MenuItems
;
2527 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2529 return NO_SELECTED_ITEM
;
2532 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2534 return NO_SELECTED_ITEM
;
2537 /* Find the start of the column */
2539 for (i
= MenuInfo
->FocusedItem
;
2540 0 != i
&& 0 == (MenuItems
[i
].fType
& MF_MENUBARBREAK
);
2548 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2549 return NO_SELECTED_ITEM
;
2552 for (--i
; 0 != i
; --i
)
2554 if (MenuItems
[i
].fType
& MF_MENUBARBREAK
)
2560 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2561 DPRINT("ret %d.\n", i
);
2566 /***********************************************************************
2569 * Return the handle of the selected sub-popup menu (if any).
2571 static HMENU FASTCALL
2572 MenuGetSubPopup(HMENU Menu
)
2574 ROSMENUINFO MenuInfo
;
2575 ROSMENUITEMINFO ItemInfo
;
2577 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2578 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2583 MenuInitRosMenuItemInfo(&ItemInfo
);
2584 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2586 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2589 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2591 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2592 return ItemInfo
.hSubMenu
;
2595 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2599 /***********************************************************************
2602 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2604 static LRESULT FASTCALL
2605 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2607 ROSMENUINFO TopMenuInfo
;
2608 ROSMENUINFO MenuInfo
;
2610 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2612 return (LRESULT
) FALSE
;
2615 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2616 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2618 MDINEXTMENU NextMenu
;
2623 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2624 NextMenu
.hmenuNext
= NULL
;
2625 NextMenu
.hwndNext
= NULL
;
2626 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2628 DPRINT("%p [%p] -> %p [%p]\n",
2629 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2631 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2633 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2634 NewWnd
= Mt
->OwnerWnd
;
2635 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2637 /* switch to the menu bar */
2639 if (0 != (Style
& WS_CHILD
)
2640 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2647 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2651 Id
= MenuInfo
.MenuItemCount
- 1;
2654 else if (0 != (Style
& WS_SYSMENU
))
2656 /* switch to the system menu */
2657 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2664 else /* application returned a new menu to switch to */
2666 NewMenu
= NextMenu
.hmenuNext
;
2667 NewWnd
= NextMenu
.hwndNext
;
2669 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2671 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2673 if (0 != (Style
& WS_SYSMENU
)
2674 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2676 /* get the real system menu */
2677 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2679 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2681 /* FIXME: Not sure what to do here;
2682 * perhaps try to track NewMenu as a popup? */
2684 DPRINT(" -- got confused.\n");
2694 if (NewMenu
!= Mt
->TopMenu
)
2696 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2698 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2700 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2704 if (NewWnd
!= Mt
->OwnerWnd
)
2706 Mt
->OwnerWnd
= NewWnd
;
2707 SetCapture(Mt
->OwnerWnd
);
2708 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2711 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2712 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2714 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2723 /***********************************************************************
2726 * The idea is not to show the popup if the next input message is
2727 * going to hide it anyway.
2729 static BOOL FASTCALL
2730 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2734 Msg
.hwnd
= Mt
->OwnerWnd
;
2736 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2737 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2742 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2743 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2745 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2746 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2747 if (WM_KEYDOWN
== Msg
.message
2748 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2750 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2757 /* failures go through this */
2758 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2763 /***********************************************************************
2766 * Handle a VK_ESCAPE key event in a menu.
2768 static BOOL FASTCALL
2769 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2771 BOOL EndMenu
= TRUE
;
2772 ROSMENUINFO MenuInfo
;
2773 HMENU MenuTmp
, MenuPrev
;
2775 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2777 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2778 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2780 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2782 /* close topmost popup */
2783 while (MenuTmp
!= Mt
->CurrentMenu
)
2786 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2789 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2791 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2793 Mt
->CurrentMenu
= MenuPrev
;
2801 /***********************************************************************
2804 * Handle a VK_LEFT key event in a menu.
2806 static void FASTCALL
2807 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2809 ROSMENUINFO MenuInfo
;
2810 ROSMENUINFO TopMenuInfo
;
2811 ROSMENUINFO PrevMenuInfo
;
2812 HMENU MenuTmp
, MenuPrev
;
2815 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2817 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2822 /* Try to move 1 column left (if possible) */
2823 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2825 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2827 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2832 /* close topmost popup */
2833 while (MenuTmp
!= Mt
->CurrentMenu
)
2836 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2839 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
2843 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
2844 Mt
->CurrentMenu
= MenuPrev
;
2846 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2850 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
2852 /* move menu bar selection if no more popups are left */
2854 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
2856 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
2859 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2861 /* A sublevel menu was displayed - display the next one
2862 * unless there is another displacement coming up */
2864 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2865 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2867 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
2874 /***********************************************************************
2877 * Handle a VK_RIGHT key event in a menu.
2879 static void FASTCALL
2880 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
2883 ROSMENUINFO MenuInfo
;
2884 ROSMENUINFO CurrentMenuInfo
;
2887 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2888 Mt
->CurrentMenu
, Mt
->TopMenu
);
2890 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2894 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
2896 /* If already displaying a popup, try to display sub-popup */
2898 MenuTmp
= Mt
->CurrentMenu
;
2899 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2901 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
2904 /* if subpopup was displayed then we are done */
2905 if (MenuTmp
!= Mt
->CurrentMenu
)
2911 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2916 /* Check to see if there's another column */
2917 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
2919 DPRINT("Going to %d.\n", NextCol
);
2920 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2922 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
2927 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
2929 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2931 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
2932 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
2939 /* try to move to the next item */
2940 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
2942 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
2945 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2947 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2948 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2950 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
2957 /***********************************************************************
2960 * Find the menu item selected by a key press.
2961 * Return item id, -1 if none, -2 if we should close the menu.
2963 static UINT FASTCALL
2964 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
2965 WCHAR Key
, BOOL ForceMenuChar
)
2967 ROSMENUINFO SysMenuInfo
;
2968 PROSMENUITEMINFO Items
, ItemInfo
;
2972 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
2974 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
2976 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
2978 MenuInfo
= &SysMenuInfo
;
2986 if (NULL
!= MenuInfo
)
2988 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
2992 if (! ForceMenuChar
)
2994 Key
= towupper(Key
);
2996 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
2998 if (IS_STRING_ITEM(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
3000 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
3003 p
= wcschr(p
+ 2, '&');
3005 while (NULL
!= p
&& L
'&' == p
[1]);
3006 if (NULL
!= p
&& (towupper(p
[1]) == Key
))
3014 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3015 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3016 if (2 == HIWORD(MenuChar
))
3018 return LOWORD(MenuChar
);
3020 if (1 == HIWORD(MenuChar
))
3029 /***********************************************************************
3032 * Menu tracking code.
3035 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3036 HWND Wnd
, const RECT
*Rect
)
3039 ROSMENUINFO MenuInfo
;
3040 ROSMENUITEMINFO ItemInfo
;
3042 INT ExecutedMenuId
= -1;
3044 BOOL EnterIdleSent
= FALSE
;
3047 Mt
.CurrentMenu
= Menu
;
3053 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3054 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3055 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3058 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3063 if (0 != (Flags
& TPM_BUTTONDOWN
))
3065 /* Get the result in order to start the tracking or not */
3066 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3067 fEndMenu
= ! fRemove
;
3070 SetCapture(Mt
.OwnerWnd
);
3071 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3075 /* we have to keep the message in the queue until it's
3076 * clear that menu loop is not over yet. */
3080 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3082 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3086 /* remove the message from the queue */
3087 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3091 if (! EnterIdleSent
)
3093 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3094 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3095 EnterIdleSent
= TRUE
;
3096 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3102 /* check if EndMenu() tried to cancel us, by posting this message */
3103 if (WM_CANCELMODE
== Msg
.message
)
3105 /* we are now out of the loop */
3108 /* remove the message from the queue */
3109 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3111 /* break out of internal loop, ala ESCAPE */
3115 TranslateMessage(&Msg
);
3118 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3120 EnterIdleSent
= FALSE
;
3124 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3127 * Use the mouse coordinates in lParam instead of those in the MSG
3128 * struct to properly handle synthetic messages. They are already
3129 * in screen coordinates.
3131 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3132 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3134 /* Find a menu for this mouse event */
3135 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3139 /* no WM_NC... messages in captured state */
3141 case WM_RBUTTONDBLCLK
:
3142 case WM_RBUTTONDOWN
:
3143 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3148 case WM_LBUTTONDBLCLK
:
3149 case WM_LBUTTONDOWN
:
3150 /* If the message belongs to the menu, removes it from the queue */
3151 /* Else, end menu tracking */
3152 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3153 fEndMenu
= ! fRemove
;
3157 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3163 /* Check if a menu was selected by the mouse */
3166 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3168 /* End the loop if ExecutedMenuId is an item ID */
3169 /* or if the job was done (ExecutedMenuId = 0). */
3170 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3174 /* No menu was selected by the mouse */
3175 /* if the function was called by TrackPopupMenu, continue
3176 with the menu tracking. If not, stop it */
3177 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3184 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3188 } /* switch(Msg.message) - mouse */
3190 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3192 fRemove
= TRUE
; /* Keyboard messages are always removed */
3200 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3202 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3208 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3210 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3211 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3215 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3216 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3218 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3220 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3222 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3226 else /* otherwise try to move selection */
3228 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3234 MenuKeyLeft(&Mt
, Flags
);
3238 MenuKeyRight(&Mt
, Flags
);
3242 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3248 hi
.cbSize
= sizeof(HELPINFO
);
3249 hi
.iContextType
= HELPINFO_MENUITEM
;
3250 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3252 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3258 MenuInitRosMenuItemInfo(&ItemInfo
);
3259 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3260 MenuInfo
.FocusedItem
,
3263 hi
.iCtrlId
= ItemInfo
.wID
;
3269 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3272 hi
.hItemHandle
= Menu
;
3273 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3274 hi
.MousePos
= Msg
.pt
;
3275 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3282 break; /* WM_KEYDOWN */
3291 break; /* WM_SYSKEYDOWN */
3297 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3301 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3303 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3304 fEndMenu
= (ExecutedMenuId
!= -1);
3308 /* Hack to avoid control chars. */
3309 /* We will find a better way real soon... */
3310 if (Msg
.wParam
< 32)
3315 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3316 LOWORD(Msg
.wParam
), FALSE
);
3317 if ((UINT
) -2 == Pos
)
3321 else if ((UINT
) -1 == Pos
)
3327 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3328 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3329 fEndMenu
= (-1 != ExecutedMenuId
);
3333 } /* switch(msg.message) - kbd */
3337 DispatchMessageW(&Msg
);
3345 /* finally remove message from the queue */
3347 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3349 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3353 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3357 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3358 SetCapture(NULL
); /* release the capture */
3360 /* If dropdown is still painted and the close box is clicked on
3361 then the menu will be destroyed as part of the DispatchMessage above.
3362 This will then invalidate the menu handle in Mt.hTopMenu. We should
3363 check for this first. */
3364 if (IsMenu(Mt
.TopMenu
))
3366 if (IsWindow(Mt
.OwnerWnd
))
3368 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3370 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3372 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3374 DestroyWindow(MenuInfo
.Wnd
);
3375 MenuInfo
.Wnd
= NULL
;
3377 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3380 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3383 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3385 /* Reset the variable for hiding menu */
3386 MenuInfo
.TimeToHide
= FALSE
;
3387 MenuSetRosMenuInfo(&MenuInfo
);
3391 /* The return value is only used by TrackPopupMenu */
3392 return (-1 != ExecutedMenuId
) ? ExecutedMenuId
: 0;
3395 /***********************************************************************
3398 static BOOL FASTCALL
3399 MenuExitTracking(HWND Wnd
)
3401 DPRINT("hwnd=%p\n", Wnd
);
3403 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3410 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3412 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3413 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3415 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3419 /* map point to parent client coordinates */
3420 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3421 if (Parent
!= GetDesktopWindow())
3423 ScreenToClient(Parent
, &Pt
);
3426 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3427 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3428 MenuExitTracking(Wnd
);
3434 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
3438 /* FUNCTIONS *****************************************************************/
3441 MenuIsStringItem(ULONG TypeData)
3443 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3451 AppendMenuA(HMENU hMenu
,
3453 UINT_PTR uIDNewItem
,
3456 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3465 AppendMenuW(HMENU hMenu
,
3467 UINT_PTR uIDNewItem
,
3470 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3479 CheckMenuItem(HMENU hmenu
,
3483 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3491 CheckMenuRadioItem(HMENU hmenu
,
3509 return NtUserCreateMenu(FALSE
);
3517 CreatePopupMenu(VOID
)
3520 return NtUserCreateMenu(TRUE
);
3528 DeleteMenu(HMENU hMenu
,
3532 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
3540 DestroyMenu(HMENU hMenu
)
3542 return NtUserDestroyMenu(hMenu
);
3550 DrawMenuBar(HWND hWnd
)
3553 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
3562 EnableMenuItem(HMENU hMenu
,
3566 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
3576 guii
.cbSize
= sizeof(GUITHREADINFO
);
3577 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
3579 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
3591 return NtUserGetMenu(hWnd
);
3599 GetMenuBarInfo(HWND hwnd
,
3604 return (BOOL
)NtUserGetMenuBarInfo(hwnd
, idObject
, idItem
, pmbi
);
3612 GetMenuCheckMarkDimensions(VOID
)
3614 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
3615 GetSystemMetrics(SM_CYMENUCHECK
)));
3623 GetMenuDefaultItem(HMENU hMenu
,
3627 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
3635 GetMenuInfo(HMENU hmenu
,
3641 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
3644 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
3645 mi
.cbSize
= sizeof(MENUINFO
);
3646 mi
.fMask
= lpcmi
->fMask
;
3648 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
3650 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
3659 GetMenuItemCount(HMENU Menu
)
3661 ROSMENUINFO MenuInfo
;
3663 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
3671 GetMenuItemID(HMENU hMenu
,
3674 ROSMENUITEMINFO mii
;
3676 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3677 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
3679 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
3684 if (NULL
!= mii
.hSubMenu
)
3705 LPMENUITEMINFOA mii
)
3710 if (mii
->cbSize
!= sizeof(MENUITEMINFOA
) &&
3711 mii
->cbSize
!= sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
))
3713 SetLastError(ERROR_INVALID_PARAMETER
);
3717 if ((mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)) == 0)
3719 /* No text requested, just pass on */
3720 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3723 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
3724 AnsiBuffer
= mii
->dwTypeData
;
3726 if (AnsiBuffer
!= NULL
)
3728 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
3729 miiW
.cch
* sizeof(WCHAR
));
3730 if (miiW
.dwTypeData
== NULL
)
3734 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)&miiW
, FALSE
))
3736 HeapFree(GetProcessHeap(), 0, miiW
.dwTypeData
);
3740 if (AnsiBuffer
!= NULL
)
3742 if (IS_STRING_ITEM(miiW
.fType
))
3744 WideCharToMultiByte(CP_ACP
, 0, miiW
.dwTypeData
, miiW
.cch
, AnsiBuffer
,
3745 mii
->cch
, NULL
, NULL
);
3747 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
3750 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
);
3751 mii
->dwTypeData
= AnsiBuffer
;
3765 LPMENUITEMINFOW mii
)
3767 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3775 GetMenuItemRect(HWND hWnd
,
3795 ROSMENUINFO MenuInfo
;
3796 ROSMENUITEMINFO mii
;
3798 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3799 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
3800 mii
.dwTypeData
= NULL
;
3803 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
3808 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
3812 nSubItems
= MenuInfo
.MenuItemCount
;
3814 /* FIXME - ported from wine, does that work (0xff)? */
3815 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
3816 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
3818 return (UINT
)-1; /* Invalid submenu */
3821 /* FIXME - ported from wine, does that work? */
3822 return (mii
.fType
| mii
.fState
);
3874 mi
.cbSize
= sizeof(MENUITEMINFOW
);
3875 mi
.fMask
= MIIM_SUBMENU
;
3877 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
3879 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
3896 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
3898 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
3913 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
3926 UINT_PTR uIDNewItem
,
3930 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3931 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
3933 mii
.fState
= MFS_ENABLED
;
3935 if(uFlags
& MF_BITMAP
)
3937 mii
.fType
|= MFT_BITMAP
;
3938 mii
.fMask
|= MIIM_BITMAP
;
3939 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
3941 else if(uFlags
& MF_OWNERDRAW
)
3943 mii
.fType
|= MFT_OWNERDRAW
;
3944 mii
.fMask
|= MIIM_DATA
;
3945 mii
.dwItemData
= (DWORD
) lpNewItem
;
3949 mii
.fMask
|= MIIM_TYPE
;
3950 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
3951 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
3954 if(uFlags
& MF_RIGHTJUSTIFY
)
3956 mii
.fType
|= MFT_RIGHTJUSTIFY
;
3958 if(uFlags
& MF_MENUBREAK
)
3960 mii
.fType
|= MFT_MENUBREAK
;
3962 if(uFlags
& MF_MENUBARBREAK
)
3964 mii
.fType
|= MFT_MENUBARBREAK
;
3966 if(uFlags
& MF_DISABLED
)
3968 mii
.fState
|= MFS_DISABLED
;
3970 if(uFlags
& MF_GRAYED
)
3972 mii
.fState
|= MFS_GRAYED
;
3975 if(uFlags
& MF_POPUP
)
3977 mii
.fType
|= MF_POPUP
;
3978 mii
.fMask
|= MIIM_SUBMENU
;
3979 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
3983 mii
.fMask
|= MIIM_ID
;
3984 mii
.wID
= (UINT
)uIDNewItem
;
3986 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
3999 LPCMENUITEMINFOA lpmii
)
4002 UNICODE_STRING MenuText
;
4004 BOOL CleanHeap
= FALSE
;
4007 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4008 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4010 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4012 /* copy the text string */
4013 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4014 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4016 Status
= RtlCreateUnicodeStringFromAsciiz(&MenuText
, (LPSTR
)mi
.dwTypeData
);
4017 if (!NT_SUCCESS (Status
))
4019 SetLastError (RtlNtStatusToDosError(Status
));
4022 mi
.dwTypeData
= MenuText
.Buffer
;
4023 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4027 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4029 if ( CleanHeap
) RtlFreeUnicodeString ( &MenuText
);
4044 LPCMENUITEMINFOW lpmii
)
4047 UNICODE_STRING MenuText
;
4049 mi
.hbmpItem
= (HBITMAP
)0;
4051 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4052 if a bad user passes bad data, we crash his process instead of the
4055 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4056 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4058 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
4060 /* copy the text string */
4061 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4062 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) &&
4063 mi
.dwTypeData
!= NULL
)
4065 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4066 mi
.dwTypeData
= MenuText
.Buffer
;
4067 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4070 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4085 UINT_PTR uIDNewItem
,
4089 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4090 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4092 mii
.fState
= MFS_ENABLED
;
4094 if(uFlags
& MF_BITMAP
)
4096 mii
.fType
|= MFT_BITMAP
;
4097 mii
.fMask
|= MIIM_BITMAP
;
4098 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4100 else if(uFlags
& MF_OWNERDRAW
)
4102 mii
.fType
|= MFT_OWNERDRAW
;
4103 mii
.fMask
|= MIIM_DATA
;
4104 mii
.dwItemData
= (DWORD
) lpNewItem
;
4108 mii
.fMask
|= MIIM_TYPE
;
4109 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4110 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4113 if(uFlags
& MF_RIGHTJUSTIFY
)
4115 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4117 if(uFlags
& MF_MENUBREAK
)
4119 mii
.fType
|= MFT_MENUBREAK
;
4121 if(uFlags
& MF_MENUBARBREAK
)
4123 mii
.fType
|= MFT_MENUBARBREAK
;
4125 if(uFlags
& MF_DISABLED
)
4127 mii
.fState
|= MFS_DISABLED
;
4129 if(uFlags
& MF_GRAYED
)
4131 mii
.fState
|= MFS_GRAYED
;
4134 if(uFlags
& MF_POPUP
)
4136 mii
.fType
|= MF_POPUP
;
4137 mii
.fMask
|= MIIM_SUBMENU
;
4138 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4142 mii
.fMask
|= MIIM_ID
;
4143 mii
.wID
= (UINT
)uIDNewItem
;
4145 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4157 ROSMENUINFO MenuInfo
;
4159 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4167 LoadMenuA(HINSTANCE hInstance
,
4170 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4171 if (Resource
== NULL
)
4175 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4183 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4185 return(LoadMenuIndirectW(lpMenuTemplate
));
4193 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4196 WORD version
, offset
;
4197 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4199 version
= GET_WORD(p
);
4204 case 0: /* standard format is version of 0 */
4205 offset
= GET_WORD(p
);
4206 p
+= sizeof(WORD
) + offset
;
4207 if (!(hMenu
= CreateMenu())) return 0;
4208 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4214 case 1: /* extended format is version of 1 */
4215 offset
= GET_WORD(p
);
4216 p
+= sizeof(WORD
) + offset
;
4217 if (!(hMenu
= CreateMenu())) return 0;
4218 if (!MENUEX_ParseResource(p
, hMenu
))
4220 DestroyMenu( hMenu
);
4225 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4235 LoadMenuW(HINSTANCE hInstance
,
4238 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4239 if (Resource
== NULL
)
4243 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4257 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4270 UINT_PTR uIDNewItem
,
4287 UINT_PTR uIDNewItem
,
4305 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
4316 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4330 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
4345 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4348 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4349 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4362 HBITMAP hBitmapUnchecked
,
4363 HBITMAP hBitmapChecked
)
4379 LPCMENUITEMINFOA lpmii
)
4381 MENUITEMINFOW MenuItemInfoW
;
4382 UNICODE_STRING UnicodeString
;
4385 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4387 if ((MenuItemInfoW
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4388 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
) &&
4389 MenuItemInfoW
.dwTypeData
!= NULL
)
4391 RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4392 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4393 MenuItemInfoW
.dwTypeData
= UnicodeString
.Buffer
;
4394 MenuItemInfoW
.cch
= UnicodeString
.Length
/ sizeof(WCHAR
);
4398 UnicodeString
.Buffer
= NULL
;
4401 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4402 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4404 if (UnicodeString
.Buffer
!= NULL
)
4406 RtlFreeUnicodeString(&UnicodeString
);
4422 LPCMENUITEMINFOW lpmii
)
4424 MENUITEMINFOW MenuItemInfoW
;
4426 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4427 MenuItemInfoW
.cch
= wcslen(MenuItemInfoW
.dwTypeData
);
4429 return NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4430 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4444 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4449 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4452 return NtUserSetSystemMenu(hwnd
, hMenu
);
4472 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
4474 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4475 if (0 == (Flags
& TPM_NONOTIFY
))
4477 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
4480 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
4482 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
4484 MenuExitTracking(Wnd
);
4503 /* Not fully implemented */
4504 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
4505 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
4514 SetMenuContextHelpId(HMENU hmenu
,
4515 DWORD dwContextHelpId
)
4517 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
4526 GetMenuContextHelpId(HMENU hmenu
)
4529 mi
.cbSize
= sizeof(ROSMENUINFO
);
4530 mi
.fMask
= MIM_HELPID
;
4532 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
4534 return mi
.dwContextHelpID
;
4579 LPCWSTR lpszNewItem
,
4584 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4585 for MF_DELETE. We should check the parameters for all others
4586 MF_* actions also (anybody got a doc on ChangeMenu?).
4589 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4592 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4595 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4598 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4601 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4602 flags
&~ MF_REMOVE
);
4604 default : /* MF_INSERT */
4605 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
4622 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4623 for MF_DELETE. We should check the parameters for all others
4624 MF_* actions also (anybody got a doc on ChangeMenu?).
4627 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4630 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4633 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4636 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4639 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4640 flags
&~ MF_REMOVE
);
4642 default : /* MF_INSERT */
4643 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);