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.
24 /* $Id: menu.c,v 1.65 2004/05/03 22:16:09 gvg Exp $
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 ******************************************************************/
44 #define NTOS_MODE_USER
47 #include <user32/callback.h>
48 #include "user32/regcontrol.h"
49 #include "../controls/controls.h"
54 /* internal popup menu window messages */
55 #define MM_SETMENUHANDLE (WM_USER + 0)
56 #define MM_GETMENUHANDLE (WM_USER + 1)
58 /* Internal MenuTrackMenu() flags */
59 #define TPM_INTERNAL 0xF0000000
60 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
61 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
62 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
64 /* TYPES *********************************************************************/
66 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
68 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
69 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
70 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
72 #define IS_SYSTEM_MENU(MenuInfo) \
73 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
75 #define IS_SYSTEM_POPUP(MenuInfo) \
76 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
78 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
80 #define MENU_BAR_ITEMS_SPACE (12)
81 #define SEPARATOR_HEIGHT (5)
82 #define MENU_TAB_SPACE (8)
88 #define MF_END (0x0080)
92 #define MIIM_STRING (0x00000040)
95 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
96 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
97 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
98 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
100 /* internal flags for menu tracking */
102 #define TF_ENDMENU 0x0001
103 #define TF_SUSPENDPOPUP 0x0002
104 #define TF_SKIPREMOVE 0x0004
109 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
110 HMENU TopMenu
; /* initial menu */
111 HWND OwnerWnd
; /* where notifications are sent */
115 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
117 /*********************************************************************
118 * PopupMenu class descriptor
120 const struct builtin_class_descr POPUPMENU_builtin_class
=
122 POPUPMENU_CLASS_ATOMW
, /* name */
123 CS_GLOBALCLASS
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
124 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
125 (WNDPROC
) NULL
, /* FIXME - procA */
126 sizeof(MENUINFO
*), /* extra */
127 (LPCWSTR
) IDC_ARROW
, /* cursor */
128 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
132 /* INTERNAL FUNCTIONS ********************************************************/
134 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
135 * Of course I didnt copy the ASM code because we want this to be portable
136 * and it needs to go away.
140 #define GET_WORD(ptr) (*(WORD *)(ptr))
143 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
146 HFONT hMenuFont
= NULL
;
147 HFONT hMenuFontBold
= NULL
;
149 /* Flag set by EndMenu() to force an exit from menu tracking */
150 static BOOL fEndMenu
= FALSE
;
152 /* Use global popup window because there's no way 2 menus can
153 * be tracked at the same time. */
154 static HWND TopPopup
;
156 /* Dimension of the menu bitmaps */
157 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
159 static HBITMAP StdMnArrow
= NULL
;
160 static HBITMAP BmpSysMenu
= NULL
;
162 /***********************************************************************
165 * Get full information about menu
168 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
170 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
171 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
173 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
176 /***********************************************************************
179 * Set full information about menu
182 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
184 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
185 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
187 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
190 /***********************************************************************
191 * MenuInitRosMenuItemInfo
193 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
196 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
198 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
199 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
202 /***********************************************************************
203 * MenuGetRosMenuItemInfo
205 * Get full information about a menu item
207 #define INITIAL_STRING_SIZE 32 /* in WCHARs */
209 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
213 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
215 /* There's already a buffer allocated */
216 Text
.Buffer
= ItemInfo
->dwTypeData
;
217 Text
.Length
= ItemInfo
->cch
* sizeof(WCHAR
);
218 Text
.MaximumLength
= (ItemInfo
->cch
< INITIAL_STRING_SIZE
? INITIAL_STRING_SIZE
219 : ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
223 Text
.Buffer
= HeapAlloc(GetProcessHeap(), 0, INITIAL_STRING_SIZE
* sizeof(WCHAR
));
224 if (NULL
== Text
.Buffer
)
229 Text
.MaximumLength
= INITIAL_STRING_SIZE
* sizeof(WCHAR
);
230 ItemInfo
->cch
= INITIAL_STRING_SIZE
- 1;
232 ItemInfo
->dwTypeData
= (LPWSTR
) &Text
;
234 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
235 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
237 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
239 if (NULL
!= Text
.Buffer
)
241 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
242 ItemInfo
->dwTypeData
= NULL
;
249 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
))
251 /* We have a string... */
252 if (Text
.MaximumLength
< (ItemInfo
->cch
+ 1) * sizeof(WCHAR
))
254 /* ...but we didn't allocate enough memory. Let's try again */
255 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
256 Text
.Buffer
= HeapAlloc(GetProcessHeap(), 0, (ItemInfo
->cch
+ 1) * sizeof(WCHAR
));
257 if (NULL
== Text
.Buffer
)
259 ItemInfo
->dwTypeData
= NULL
;
263 Text
.Length
= (ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
264 Text
.MaximumLength
= (ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
266 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
268 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
269 ItemInfo
->dwTypeData
= NULL
;
276 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
))
278 ItemInfo
->dwTypeData
= Text
.Buffer
;
279 ItemInfo
->cch
= Text
.Length
/ sizeof(WCHAR
);
280 Text
.Buffer
[ItemInfo
->cch
] = L
'\0';
284 /* Not a string, clean up the buffer */
285 if (NULL
!= Text
.Buffer
)
287 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
294 /***********************************************************************
295 * MenuSetRosMenuItemInfo
297 * Set full information about a menu item
300 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
306 StringVal
= (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
);
309 Text
.Buffer
= ItemInfo
->dwTypeData
;
310 Text
.Length
= wcslen(Text
.Buffer
) * sizeof(WCHAR
);
311 Text
.MaximumLength
= Text
.Length
+ sizeof(WCHAR
);
312 ItemInfo
->dwTypeData
= (LPWSTR
) &Text
;
314 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
315 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
317 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
321 ItemInfo
->dwTypeData
= Text
.Buffer
;
327 /***********************************************************************
328 * MenuCleanupRosMenuItemInfo
330 * Cleanup after use of MenuGet/SetRosMenuItemInfo
333 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
335 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
337 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
338 ItemInfo
->dwTypeData
= NULL
;
343 /***********************************************************************
344 * MenuGetAllRosMenuItemInfo
346 * Get full information about all menu items
349 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
353 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
358 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
359 if (NULL
== *ItemInfo
)
364 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
367 /***********************************************************************
368 * MenuCleanupAllRosMenuItemInfo
370 * Cleanup after use of MenuGetAllRosMenuItemInfo
373 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
375 HeapFree(GetProcessHeap(), 0, ItemInfo
);
379 /***********************************************************************
382 * Load the arrow bitmap. We can't do this from MenuInit since user32
383 * can also be used (and thus initialized) from text-mode.
386 MenuLoadBitmaps(VOID
)
388 /* Load menu bitmaps */
389 if (NULL
== StdMnArrow
)
391 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
393 if (NULL
!= StdMnArrow
)
396 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
397 ArrowBitmapWidth
= bm
.bmWidth
;
398 ArrowBitmapHeight
= bm
.bmHeight
;
402 /* Load system buttons bitmaps */
403 if (NULL
== BmpSysMenu
)
405 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
409 /***********************************************************************
410 * MenuGetBitmapItemSize
412 * Get the size of a bitmap item.
415 MenuGetBitmapItemSize(UINT Id
, DWORD Data
, SIZE
*Size
)
418 HBITMAP Bmp
= (HBITMAP
) Id
;
420 Size
->cx
= Size
->cy
= 0;
422 /* check if there is a magic menu item associated with this item */
423 if (0 != Id
&& IS_MAGIC_ITEM(Id
))
427 case (INT_PTR
) HBMMENU_SYSTEM
:
430 Bmp
= (HBITMAP
) Data
;
434 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
435 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
436 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
437 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
438 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
439 Size
->cx
= GetSystemMetrics(SM_CXSIZE
);
440 Size
->cy
= GetSystemMetrics(SM_CYSIZE
);
442 case (INT_PTR
) HBMMENU_CALLBACK
:
443 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
444 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
445 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
446 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
448 FIXME("Magic menu bitmap not implemented\n");
453 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
455 Size
->cx
= Bm
.bmWidth
;
456 Size
->cy
= Bm
.bmHeight
;
460 /***********************************************************************
463 * Draw a bitmap item.
466 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
, BOOL MenuBar
)
471 HBITMAP Bmp
= (HBITMAP
) Item
->hbmpItem
;
472 int w
= Rect
->right
- Rect
->left
;
473 int h
= Rect
->bottom
- Rect
->top
;
477 /* Check if there is a magic menu item associated with this item */
478 if (IS_MAGIC_ITEM(Item
->hbmpItem
))
483 switch ((int) Item
->hbmpItem
)
485 case (INT_PTR
) HBMMENU_SYSTEM
:
486 if (NULL
!= Item
->hbmpItem
)
488 Bmp
= Item
->hbmpItem
;
489 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
497 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
501 /* only use right half of the bitmap */
502 BmpXoffset
= Bm
.bmWidth
/ 2;
503 Bm
.bmWidth
-= BmpXoffset
;
506 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
507 Flags
= DFCS_CAPTIONRESTORE
;
509 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
510 Flags
= DFCS_CAPTIONMIN
;
512 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
513 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
515 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
516 Flags
= DFCS_CAPTIONCLOSE
;
518 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
519 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
521 case (INT_PTR
) HBMMENU_CALLBACK
:
522 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
523 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
524 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
525 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
527 FIXME("Magic menu bitmap not implemented\n");
531 InflateRect(&r
, -1, -1);
532 if (0 != (Item
->fState
& MF_HILITE
))
534 Flags
|= DFCS_PUSHED
;
536 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
540 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
546 DcMem
= CreateCompatibleDC(Dc
);
547 SelectObject(DcMem
, Bmp
);
549 /* handle fontsize > bitmap_height */
550 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
552 Rop
= (0 != (Item
->fState
& MF_HILITE
) && ! IS_MAGIC_ITEM(Item
->hbmpItem
)) ? NOTSRCCOPY
: SRCCOPY
;
553 if (0 != (Item
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(Item
->fType
))
555 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
557 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
561 /***********************************************************************
564 * Draw a single menu item.
567 MenuDrawMenuItem(HWND Wnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
568 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
573 if (0 != (Item
->fType
& MF_SYSMENU
))
577 UserGetInsideRectNC(Wnd
, &Rect
);
578 UserDrawSysMenuButton(Wnd
, Dc
, &Rect
,
579 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
587 if (0 != (Item
->fState
& MF_HILITE
))
591 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
592 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
596 if (0 != (Item
->fState
& MF_GRAYED
))
598 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
602 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
604 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
609 if (0 != (Item
->fState
& MF_GRAYED
))
611 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
615 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
617 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
620 if (0 != (Item
->fType
& MF_OWNERDRAW
))
623 ** Experimentation under Windows reveals that an owner-drawn
624 ** menu is given the rectangle which includes the space it requested
625 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
626 ** and a popup-menu arrow. This is the value of lpitem->rect.
627 ** Windows will leave all drawing to the application except for
628 ** the popup-menu arrow. Windows always draws that itself, after
629 ** the menu owner has finished drawing.
633 dis
.CtlType
= ODT_MENU
;
635 dis
.itemID
= Item
->wID
;
636 dis
.itemData
= (DWORD
)Item
->dwItemData
;
638 if (0 != (Item
->fState
& MF_CHECKED
))
640 dis
.itemState
|= ODS_CHECKED
;
642 if (0 != (Item
->fState
& MF_GRAYED
))
644 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
646 if (0 != (Item
->fState
& MF_HILITE
))
648 dis
.itemState
|= ODS_SELECTED
;
650 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
651 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
653 dis
.rcItem
= Item
->Rect
;
654 DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
655 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
656 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
657 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
659 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
660 /* Fall through to draw popup-menu arrow */
663 DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
664 Item
->Rect
.right
, Item
->Rect
.bottom
);
666 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
673 if (0 == (Item
->fType
& MF_OWNERDRAW
))
675 if (Item
->fState
& MF_HILITE
)
679 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
683 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
688 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENU
));
692 SetBkMode(Dc
, TRANSPARENT
);
694 if (0 == (Item
->fType
& MF_OWNERDRAW
))
696 /* vertical separator */
697 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
701 rc
.bottom
= Height
- 3;
702 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
705 /* horizontal separator */
706 if (0 != (Item
->fType
& MF_SEPARATOR
))
711 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
712 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
719 /* helper lines for debugging */
720 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
721 SelectObject(Dc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
));
722 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
723 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
728 INT y
= Rect
.top
+ Rect
.bottom
;
729 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
730 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
732 if (0 == (Item
->fType
& MF_OWNERDRAW
))
734 /* Draw the check mark
737 * Custom checkmark bitmaps are monochrome but not always 1bpp.
739 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
740 if (NULL
!= bm
) /* we have a custom bitmap */
742 HDC DcMem
= CreateCompatibleDC(Dc
);
743 SelectObject(DcMem
, bm
);
744 BitBlt(Dc
, Rect
.left
, (y
- CheckBitmapHeight
) / 2,
745 CheckBitmapWidth
, CheckBitmapHeight
,
746 DcMem
, 0, 0, SRCCOPY
);
749 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
752 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
753 HDC DcMem
= CreateCompatibleDC(Dc
);
754 SelectObject(DcMem
, bm
);
755 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
756 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
757 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
758 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
759 BitBlt(Dc
, Rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
760 DcMem
, 0, 0, SRCCOPY
);
766 /* Draw the popup-menu arrow */
767 if (0 != (Item
->fType
& MF_POPUP
))
769 HDC DcMem
= CreateCompatibleDC(Dc
);
772 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
773 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
774 (y
- ArrowBitmapHeight
) / 2,
775 ArrowBitmapWidth
, ArrowBitmapHeight
,
776 DcMem
, 0, 0, SRCCOPY
);
777 SelectObject(DcMem
, OrigBitmap
);
781 Rect
.left
+= CheckBitmapWidth
;
782 Rect
.right
-= ArrowBitmapWidth
;
785 /* Done for owner-drawn */
786 if (0 != (Item
->fType
& MF_OWNERDRAW
))
791 /* Draw the item text or bitmap */
792 if (IS_BITMAP_ITEM(Item
->fType
))
794 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuBar
);
797 /* No bitmap - process text if present */
798 else if (IS_STRING_ITEM(Item
->fType
))
801 HFONT FontOld
= NULL
;
803 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
804 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
806 if (0 != (Item
->fState
& MFS_DEFAULT
))
808 FontOld
= SelectObject(Dc
, hMenuFontBold
);
813 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
814 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
817 Text
= (PWCHAR
) Item
->dwTypeData
;
818 for (i
= 0; L
'\0' != Text
[i
]; i
++)
820 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
826 if (0 != (Item
->fState
& MF_GRAYED
))
828 if (0 == (Item
->fState
& MF_HILITE
))
830 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
831 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
832 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
833 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
835 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
838 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
840 /* paint the shortcut text */
841 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
843 if (L
'\t' == Text
[i
])
845 Rect
.left
= Item
->XTab
;
846 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
850 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
853 if (0 != (Item
->fState
& MF_GRAYED
))
855 if (0 == (Item
->fState
& MF_HILITE
))
857 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
858 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
859 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
860 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
862 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
864 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
869 SelectObject(Dc
, FontOld
);
874 /***********************************************************************
877 * Paint a popup menu.
880 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
882 HBRUSH PrevBrush
= NULL
;
885 ROSMENUINFO MenuInfo
;
886 ROSMENUITEMINFO ItemInfo
;
889 DPRINT("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
891 GetClientRect(Wnd
, &Rect
);
893 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
894 && NULL
!= SelectObject(Dc
, hMenuFont
))
896 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
898 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
901 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
903 /* draw menu items */
905 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
907 MenuInitRosMenuItemInfo(&ItemInfo
);
909 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
911 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
913 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
914 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
921 SelectObject(Dc
, PrevBrush
);
926 static LRESULT WINAPI
927 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
929 DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
935 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
936 SetWindowLongW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
940 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
941 return MA_NOACTIVATE
;
946 BeginPaint(Wnd
, &ps
);
947 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongW(Wnd
, 0));
956 /* zero out global pointer in case resident popup window was destroyed. */
966 if (0 == GetWindowLongW(Wnd
, 0))
968 OutputDebugStringA("no menu to display\n");
973 SetWindowLongW(Wnd
, 0, 0);
977 case MM_SETMENUHANDLE
:
978 SetWindowLongW(Wnd
, 0, wParam
);
981 case MM_GETMENUHANDLE
:
982 return GetWindowLongW(Wnd
, 0);
985 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
991 /**********************************************************************
992 * MENUEX_ParseResource
994 * Parse an extended menu resource and add items to the menu.
995 * Return a pointer to the end of the resource.
997 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
999 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1007 mii
.cbSize
= sizeof(mii
);
1008 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
1009 mii
.fType
= GET_DWORD(res
);
1010 res
+= sizeof(DWORD
);
1011 mii
.fState
= GET_DWORD(res
);
1012 res
+= sizeof(DWORD
);
1013 mii
.wID
= GET_DWORD(res
);
1014 res
+= sizeof(DWORD
);
1015 resinfo
= GET_WORD(res
);
1016 res
+= sizeof(WORD
);
1017 /* Align the text on a word boundary. */
1018 res
+= (~((int)res
- 1)) & 1;
1019 mii
.dwTypeData
= (LPWSTR
) res
;
1020 res
+= (1 + wcslen(mii
.dwTypeData
)) * sizeof(WCHAR
);
1021 /* Align the following fields on a dword boundary. */
1022 res
+= (~((int)res
- 1)) & 3;
1024 if (resinfo
& 1) /* Pop-up? */
1026 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1027 res
+= sizeof(DWORD
);
1028 mii
.hSubMenu
= CreatePopupMenu();
1031 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
1033 DestroyMenu(mii
.hSubMenu
);
1036 mii
.fMask
|= MIIM_SUBMENU
;
1037 mii
.fType
|= MF_POPUP
;
1039 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1041 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1042 mii
.wID
, mii
.fType
);
1043 mii
.fType
|= MF_SEPARATOR
;
1045 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1047 while (!(resinfo
& MF_END
));
1052 /**********************************************************************
1053 * MENU_ParseResource
1055 * Parse a standard menu resource and add items to the menu.
1056 * Return a pointer to the end of the resource.
1058 * NOTE: flags is equivalent to the mtOption field
1060 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1069 flags
= GET_WORD(res
);
1071 /* remove MF_END flag before passing it to AppendMenu()! */
1072 end
= (flags
& MF_END
);
1073 if(end
) flags
^= MF_END
;
1075 res
+= sizeof(WORD
);
1076 if(!(flags
& MF_POPUP
))
1079 res
+= sizeof(WORD
);
1083 res
+= strlen(str
) + 1;
1085 res
+= (wcslen((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1086 if (flags
& MF_POPUP
)
1088 hSubMenu
= CreatePopupMenu();
1089 if(!hSubMenu
) return NULL
;
1090 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1093 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1095 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1097 else /* Not a popup */
1100 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1102 AppendMenuW(hMenu
, flags
, id
,
1103 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1112 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1116 hUser32
= GetModuleHandleW(L
"USER32");
1117 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
1118 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1125 NONCLIENTMETRICSW ncm
;
1127 /* get the menu font */
1128 if(!hMenuFont
|| !hMenuFontBold
)
1130 ncm
.cbSize
= sizeof(ncm
);
1131 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1133 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1137 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1138 if(hMenuFont
== NULL
)
1140 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1144 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1145 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1146 if(hMenuFontBold
== NULL
)
1148 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1157 /***********************************************************************
1160 * Calculate the size of the menu item and store it in ItemInfo->rect.
1162 static void FASTCALL
1163 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, HWND WndOwner
,
1164 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1167 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1169 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1171 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1173 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1176 ** Experimentation under Windows reveals that an owner-drawn
1177 ** menu is expected to return the size of the content part of
1178 ** the menu item, not including the checkmark nor the submenu
1179 ** arrow. Windows adds those values itself and returns the
1180 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1182 MEASUREITEMSTRUCT mis
;
1183 mis
.CtlType
= ODT_MENU
;
1185 mis
.itemID
= ItemInfo
->wID
;
1186 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1189 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1190 ItemInfo
->Rect
.right
+= mis
.itemWidth
;
1194 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1196 /* under at least win95 you seem to be given a standard
1197 height for the menu and the height value is ignored */
1199 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENU
) - 1;
1203 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1206 DPRINT("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1207 /* Fall through to get check/arrow width calculation. */
1210 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1212 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1218 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1219 if (0 != (ItemInfo
->fType
& MF_POPUP
))
1221 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1225 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1230 if (IS_BITMAP_ITEM(ItemInfo
->fType
))
1234 MenuGetBitmapItemSize((int) ItemInfo
->hbmpItem
, (DWORD
) ItemInfo
->hbmpItem
, &Size
);
1235 ItemInfo
->Rect
.right
+= Size
.cx
;
1236 ItemInfo
->Rect
.bottom
+= Size
.cy
;
1238 /* Leave space for the sunken border */
1239 ItemInfo
->Rect
.right
+= 2;
1240 ItemInfo
->Rect
.bottom
+= 2;
1243 /* it must be a text item - unless it's the system menu */
1244 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && IS_STRING_ITEM(ItemInfo
->fType
))
1248 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1249 wcslen((LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1251 ItemInfo
->Rect
.right
+= Size
.cx
;
1252 ItemInfo
->Rect
.bottom
+= max(Size
.cy
, GetSystemMetrics(SM_CYMENU
) - 1);
1257 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1259 else if ((p
= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\t' )) != NULL
)
1261 /* Item contains a tab (only meaningful in popup menus) */
1262 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1263 (int)(p
- (LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1264 ItemInfo
->XTab
= CheckBitmapWidth
+ MENU_TAB_SPACE
+ Size
.cx
;
1265 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1269 if (NULL
!= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\b'))
1271 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1273 ItemInfo
->XTab
= ItemInfo
->Rect
.right
- CheckBitmapWidth
1278 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1281 /***********************************************************************
1282 * MenuPopupMenuCalcSize
1284 * Calculate the size of a popup menu.
1286 static void FASTCALL
1287 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1289 ROSMENUITEMINFO ItemInfo
;
1292 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1294 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1295 if (0 == MenuInfo
->MenuItemCount
)
1297 MenuSetRosMenuInfo(MenuInfo
);
1302 SelectObject(Dc
, hMenuFont
);
1307 MenuInitRosMenuItemInfo(&ItemInfo
);
1308 while (Start
< MenuInfo
->MenuItemCount
)
1313 MaxTab
= MaxTabWidth
= 0;
1315 /* Parse items until column break or end of menu */
1316 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1318 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1320 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1321 MenuSetRosMenuInfo(MenuInfo
);
1325 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1330 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1331 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1333 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1334 MenuSetRosMenuInfo(MenuInfo
);
1338 if (0 != (ItemInfo
.fType
& MF_MENUBARBREAK
))
1342 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1343 OrgY
= ItemInfo
.Rect
.bottom
;
1344 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1346 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1347 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1351 /* Finish the column (set all items to the largest width found) */
1352 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1355 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1357 ItemInfo
.Rect
.right
= MaxX
;
1358 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1360 ItemInfo
.XTab
= MaxTab
;
1362 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1366 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1369 MenuInfo
->Width
= MaxX
;
1371 /* space for 3d border */
1372 MenuInfo
->Height
+= 2;
1373 MenuInfo
->Width
+= 2;
1375 ReleaseDC(NULL
, Dc
);
1376 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1377 MenuSetRosMenuInfo(MenuInfo
);
1380 /***********************************************************************
1381 * MenuMenuBarCalcSize
1383 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1384 * height is off by 1 pixel which causes lengthy window relocations when
1385 * active document window is maximized/restored.
1387 * Calculate the size of the menu bar.
1389 static void FASTCALL
1390 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1392 ROSMENUITEMINFO ItemInfo
;
1393 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1395 if (NULL
== Rect
|| NULL
== MenuInfo
)
1399 if (0 == MenuInfo
->MenuItemCount
)
1404 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1405 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1406 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1407 MenuInfo
->Height
= 0;
1408 MaxY
= Rect
->top
+ 1;
1411 MenuInitRosMenuItemInfo(&ItemInfo
);
1412 while (Start
< MenuInfo
->MenuItemCount
)
1414 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1416 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1422 /* Parse items until line break or end of menu */
1423 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1425 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1430 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1435 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1436 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1437 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1439 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1443 if (ItemInfo
.Rect
.right
> Rect
->right
)
1451 ItemInfo
.Rect
.right
= Rect
->right
;
1454 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1455 OrgX
= ItemInfo
.Rect
.right
;
1456 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1458 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1460 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1466 /* Finish the line (set all items to the largest height found) */
1469 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1471 ItemInfo
.Rect
.bottom
= MaxY
;
1472 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1478 Rect
->bottom
= MaxY
;
1479 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1480 MenuSetRosMenuInfo(MenuInfo
);
1484 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1485 /* the last item (if several lines, only move the last line) */
1486 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1488 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1491 OrgY
= ItemInfo
.Rect
.top
;
1493 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1499 if (ItemInfo
.Rect
.top
!= OrgY
)
1501 break; /* Other line */
1503 if (OrgX
<= ItemInfo
.Rect
.right
)
1505 break; /* Too far right already */
1507 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1508 ItemInfo
.Rect
.right
= OrgX
;
1509 OrgX
= ItemInfo
.Rect
.left
;
1510 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1511 if (HelpPos
+ 1 <= i
&&
1512 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1514 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1520 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1523 /***********************************************************************
1524 * DrawMenuBarTemp (USER32.@)
1528 * called by W98SE desk.cpl Control Panel Applet
1530 * Not 100% sure about the param names, but close.
1535 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1537 ROSMENUINFO MenuInfo
;
1538 ROSMENUITEMINFO ItemInfo
;
1540 HFONT FontOld
= NULL
;
1544 Menu
= GetMenu(Wnd
);
1552 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1554 return GetSystemMetrics(SM_CYMENU
);
1557 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1559 FontOld
= SelectObject(DC
, Font
);
1561 if (0 == MenuInfo
.Height
)
1563 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1566 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1568 FillRect(DC
, Rect
, GetSysColorBrush(COLOR_MENU
));
1570 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1571 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1572 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1574 if (0 == MenuInfo
.MenuItemCount
)
1576 SelectObject(DC
, FontOld
);
1577 return GetSystemMetrics(SM_CYMENU
);
1580 MenuInitRosMenuItemInfo(&ItemInfo
);
1581 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1583 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1585 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1586 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1589 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1591 SelectObject(DC
, FontOld
);
1593 return MenuInfo
.Height
;
1597 /***********************************************************************
1600 * Paint a menu bar. Returns the height of the menu bar.
1601 * called from [windows/nonclient.c]
1603 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1605 ROSMENUINFO MenuInfo
;
1606 HFONT FontOld
= NULL
;
1607 HMENU Menu
= GetMenu(Wnd
);
1609 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1611 return GetSystemMetrics(SM_CYMENU
);
1616 FontOld
= SelectObject(DC
, hMenuFont
);
1618 if (0 == MenuInfo
.Height
)
1620 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1623 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1625 if (NULL
!= FontOld
)
1627 SelectObject(DC
, FontOld
);
1629 return MenuInfo
.Height
;
1633 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1637 /***********************************************************************
1640 static BOOL FASTCALL
1641 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1643 DPRINT("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1647 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1648 if (0 == (Flags
& TPM_NONOTIFY
))
1650 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1653 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1655 if (0 == (Flags
& TPM_NONOTIFY
))
1657 ROSMENUINFO MenuInfo
;
1659 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1661 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 == MenuInfo
.Height
)
1663 /* app changed/recreated menu bar entries in WM_INITMENU
1664 Recalculate menu sizes else clicks will not work */
1665 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1666 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1675 /***********************************************************************
1678 * Display a popup menu.
1680 static BOOL FASTCALL
1681 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1682 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1684 ROSMENUINFO MenuInfo
;
1685 ROSMENUITEMINFO ItemInfo
;
1688 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1689 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1691 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1696 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1698 MenuInitRosMenuItemInfo(&ItemInfo
);
1699 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1701 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1702 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1704 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1705 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1708 /* store the owner for DrawItem */
1709 MenuInfo
.WndOwner
= WndOwner
;
1710 MenuSetRosMenuInfo(&MenuInfo
);
1712 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1714 /* adjust popup menu pos so that it fits within the desktop */
1716 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1717 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1719 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1723 X
-= Width
- XAnchor
;
1725 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1727 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1735 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1739 Y
-= Height
+ YAnchor
;
1741 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1743 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1752 /* NOTE: In Windows, top menu popup is not owned. */
1753 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1754 WS_POPUP
, X
, Y
, Width
, Height
,
1755 WndOwner
, 0, (HINSTANCE
) GetWindowLongW(WndOwner
, GWL_HINSTANCE
),
1756 (LPVOID
) MenuInfo
.Self
);
1757 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1761 if (NULL
== TopPopup
)
1763 TopPopup
= MenuInfo
.Wnd
;
1766 /* Display the window */
1767 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1768 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1769 UpdateWindow(MenuInfo
.Wnd
);
1774 /***********************************************************************
1777 * Find a Sub menu. Return the position of the submenu, and modifies
1778 * *hmenu in case it is found in another sub-menu.
1779 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1781 static UINT FASTCALL
1782 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1784 ROSMENUINFO MenuInfo
;
1785 ROSMENUITEMINFO ItemInfo
;
1790 if ((HMENU
) 0xffff == *Menu
1791 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1793 return NO_SELECTED_ITEM
;
1796 MenuInitRosMenuItemInfo(&ItemInfo
);
1797 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1799 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1801 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1802 return NO_SELECTED_ITEM
;
1804 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1808 if (ItemInfo
.hSubMenu
== SubTarget
)
1810 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1813 SubMenu
= ItemInfo
.hSubMenu
;
1814 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1815 if (NO_SELECTED_ITEM
!= Pos
)
1821 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1823 return NO_SELECTED_ITEM
;
1826 /***********************************************************************
1829 static void FASTCALL
1830 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1831 BOOL SendMenuSelect
, HMENU TopMenu
)
1834 ROSMENUITEMINFO ItemInfo
;
1835 ROSMENUINFO TopMenuInfo
;
1838 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1840 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1845 if (MenuInfo
->FocusedItem
== Index
)
1850 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1852 Dc
= GetDC(MenuInfo
->Wnd
);
1856 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1859 if (NULL
== TopPopup
)
1861 TopPopup
= MenuInfo
->Wnd
;
1864 SelectObject(Dc
, hMenuFont
);
1865 MenuInitRosMenuItemInfo(&ItemInfo
);
1867 /* Clear previous highlighted item */
1868 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1870 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1872 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1873 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1875 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1876 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1880 /* Highlight new item (if any) */
1881 MenuInfo
->FocusedItem
= Index
;
1882 MenuSetRosMenuInfo(MenuInfo
);
1883 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1885 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1887 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1889 ItemInfo
.fState
|= MF_HILITE
;
1890 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1891 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
1892 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1897 SendMessageW(WndOwner
, WM_MENUSELECT
,
1898 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
1899 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1900 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
1904 else if (SendMenuSelect
)
1906 if (NULL
!= TopMenu
)
1908 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
1909 if (NO_SELECTED_ITEM
!= Pos
)
1911 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
1912 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
1914 SendMessageW(WndOwner
, WM_MENUSELECT
,
1915 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1917 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1924 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1925 ReleaseDC(MenuInfo
->Wnd
, Dc
);
1928 /***********************************************************************
1931 * Moves currently selected item according to the Offset parameter.
1932 * If there is no selection then it should select the last item if
1933 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1935 static void FASTCALL
1936 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1939 ROSMENUITEMINFO ItemInfo
;
1941 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1943 MenuInitRosMenuItemInfo(&ItemInfo
);
1944 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1946 if (1 == MenuInfo
->MenuItemCount
)
1948 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1953 for (i
= MenuInfo
->FocusedItem
+ Offset
;
1954 0 <= i
&& i
< MenuInfo
->MenuItemCount
;
1957 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1958 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1960 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1961 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1968 for (i
= (0 < Offset
) ? 0 : MenuInfo
->MenuItemCount
- 1;
1969 0 <= i
&& i
< MenuInfo
->MenuItemCount
; i
+= Offset
)
1971 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1972 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1974 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1975 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1980 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1983 /***********************************************************************
1984 * MenuInitSysMenuPopup
1986 * Grey the appropriate items in System menu.
1989 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
1997 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1998 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
1999 Gray
= 0 != (Style
& WS_MAXIMIZE
);
2000 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2001 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
2002 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2003 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
2004 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2005 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
2006 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2007 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
2009 /* The menu item must keep its state if it's disabled */
2012 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
2015 /* Set default menu item */
2016 if(Style
& WS_MINIMIZE
)
2018 DefItem
= SC_RESTORE
;
2022 if(HitTest
== HTCAPTION
)
2024 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
2032 mii
.cbSize
= sizeof(MENUITEMINFOW
);
2033 mii
.fMask
= MIIM_STATE
;
2034 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
2035 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
2040 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
2043 /***********************************************************************
2046 * Display the sub-menu of the selected item of this menu.
2047 * Return the handle of the submenu, or menu if no submenu to display.
2049 static HMENU FASTCALL
2050 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2052 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2054 ROSMENUITEMINFO ItemInfo
;
2055 ROSMENUINFO SubMenuInfo
;
2059 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2061 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2063 return MenuInfo
->Self
;
2066 MenuInitRosMenuItemInfo(&ItemInfo
);
2067 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2069 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2070 return MenuInfo
->Self
;
2072 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2074 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2075 return MenuInfo
->Self
;
2078 /* message must be sent before using item,
2079 because nearly everything may be changed by the application ! */
2081 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2082 if (0 == (Flags
& TPM_NONOTIFY
))
2084 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2085 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2088 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2090 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2091 return MenuInfo
->Self
;
2093 Rect
= ItemInfo
.Rect
;
2095 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2096 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2098 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2100 Dc
= GetDC(MenuInfo
->Wnd
);
2104 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2107 SelectObject(Dc
, hMenuFont
);
2109 ItemInfo
.fState
|= MF_HILITE
;
2110 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2111 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2112 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2113 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2116 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2117 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2119 ItemInfo
.Rect
= Rect
;
2122 ItemInfo
.fState
|= MF_MOUSESELECT
;
2124 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2126 if (IS_SYSTEM_MENU(MenuInfo
))
2128 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2129 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2131 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2132 Rect
.top
= Rect
.bottom
;
2133 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2134 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2138 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2139 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2141 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2142 Rect
.top
+= ItemInfo
.Rect
.top
;
2143 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2144 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
;
2148 Rect
.left
+= ItemInfo
.Rect
.left
;
2149 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2150 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2151 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2155 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2156 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2157 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2159 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2162 Ret
= ItemInfo
.hSubMenu
;
2163 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2168 /***********************************************************************
2171 * Hide the sub-popup menus of this menu.
2173 static void FASTCALL
2174 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2176 ROSMENUINFO SubMenuInfo
;
2177 ROSMENUITEMINFO ItemInfo
;
2179 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2181 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2183 MenuInitRosMenuItemInfo(&ItemInfo
);
2184 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2185 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2186 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2188 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2191 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2192 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2193 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2195 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2196 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2197 DestroyWindow(SubMenuInfo
.Wnd
);
2198 SubMenuInfo
.Wnd
= NULL
;
2199 MenuSetRosMenuInfo(&SubMenuInfo
);
2204 /***********************************************************************
2205 * MenuSwitchTracking
2207 * Helper function for menu navigation routines.
2209 static void FASTCALL
2210 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2212 ROSMENUINFO TopMenuInfo
;
2214 DPRINT("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2216 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2217 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2218 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2220 /* both are top level menus (system and menu-bar) */
2221 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2222 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2223 Mt
->TopMenu
= PtMenuInfo
->Self
;
2227 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2230 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2233 /***********************************************************************
2234 * MenuExecFocusedItem
2236 * Execute a menu item (for instance when user pressed Enter).
2237 * Return the wID of the executed item. Otherwise, -1 indicating
2238 * that no menu item was executed;
2239 * Have to receive the flags for the TrackPopupMenu options to avoid
2240 * sending unwanted message.
2244 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2246 ROSMENUITEMINFO ItemInfo
;
2249 DPRINT("%p menu=%p\n", Mt
, MenuInfo
);
2251 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2256 MenuInitRosMenuItemInfo(&ItemInfo
);
2257 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2259 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2263 DPRINT("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2265 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2267 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2268 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2270 /* If TPM_RETURNCMD is set you return the id, but
2271 do not send a message to the owner */
2272 if (0 == (Flags
& TPM_RETURNCMD
))
2274 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2276 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2277 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2281 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2285 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2291 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2297 /***********************************************************************
2300 * Return TRUE if we can go on with menu tracking.
2302 static BOOL FASTCALL
2303 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2306 ROSMENUINFO MenuInfo
;
2307 ROSMENUITEMINFO Item
;
2309 DPRINT("%x PtMenu=%p\n", Mt
, PtMenu
);
2313 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2317 if (IS_SYSTEM_MENU(&MenuInfo
))
2323 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2325 MenuInitRosMenuItemInfo(&Item
);
2326 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2328 MenuCleanupRosMenuItemInfo(&Item
);
2332 if (MenuInfo
.FocusedItem
!= Index
)
2334 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2337 /* If the popup menu is not already "popped" */
2338 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2340 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2343 MenuCleanupRosMenuItemInfo(&Item
);
2348 /* else the click was on the menu bar, finish the tracking */
2353 /***********************************************************************
2356 * Return the value of MenuExecFocusedItem if
2357 * the selected item was not a popup. Else open the popup.
2358 * A -1 return value indicates that we go on with menu tracking.
2362 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2365 ROSMENUINFO MenuInfo
;
2366 ROSMENUITEMINFO ItemInfo
;
2368 DPRINT("%p hmenu=%x\n", Mt
, PtMenu
);
2373 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2378 if (! IS_SYSTEM_MENU(&MenuInfo
))
2380 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2382 MenuInitRosMenuItemInfo(&ItemInfo
);
2383 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2384 MenuInfo
.FocusedItem
== Id
)
2386 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2388 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2389 return MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2391 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2393 /* If we are dealing with the top-level menu */
2394 /* and this is a click on an already "popped" item: */
2395 /* Stop the menu tracking and close the opened submenus */
2396 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2398 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2402 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2403 MenuInfo
.TimeToHide
= TRUE
;
2404 MenuSetRosMenuInfo(&MenuInfo
);
2410 /***********************************************************************
2413 * Walks menu chain trying to find a menu pt maps to.
2415 static HMENU FASTCALL
2416 MenuPtMenu(HMENU Menu
, POINT Pt
)
2418 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2419 ROSMENUINFO MenuInfo
;
2420 ROSMENUITEMINFO ItemInfo
;
2424 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2429 /* try subpopup first (if any) */
2430 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2432 MenuInitRosMenuItemInfo(&ItemInfo
);
2433 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2434 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2435 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2437 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2440 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2444 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2447 /* check the current window (avoiding WM_HITTEST) */
2448 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2449 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2451 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2456 else if (HTSYSMENU
== Ht
)
2458 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2460 else if (HTMENU
== Ht
)
2462 Ret
= GetMenu(MenuInfo
.Wnd
);
2468 /***********************************************************************
2471 * Return TRUE if we can go on with menu tracking.
2473 static BOOL FASTCALL
2474 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2477 ROSMENUINFO MenuInfo
;
2481 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2485 if (IS_SYSTEM_MENU(&MenuInfo
))
2491 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2496 Index
= NO_SELECTED_ITEM
;
2499 if (NO_SELECTED_ITEM
== Index
)
2501 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2502 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2504 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2508 else if (MenuInfo
.FocusedItem
!= Index
)
2510 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2511 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2517 /******************************************************************************
2519 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2521 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2524 PROSMENUITEMINFO MenuItems
;
2526 i
= MenuInfo
->FocusedItem
;
2527 if (NO_SELECTED_ITEM
== i
)
2532 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2534 return NO_SELECTED_ITEM
;
2537 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2539 if (0 != (MenuItems
[i
].fType
& MF_MENUBARBREAK
))
2545 return NO_SELECTED_ITEM
;
2548 /******************************************************************************
2550 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2552 static UINT FASTCALL
2553 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2556 PROSMENUITEMINFO MenuItems
;
2558 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2560 return NO_SELECTED_ITEM
;
2563 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2565 return NO_SELECTED_ITEM
;
2568 /* Find the start of the column */
2570 for (i
= MenuInfo
->FocusedItem
;
2571 0 != i
&& 0 == (MenuItems
[i
].fType
& MF_MENUBARBREAK
);
2579 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2580 return NO_SELECTED_ITEM
;
2583 for (--i
; 0 != i
; --i
)
2585 if (MenuItems
[i
].fType
& MF_MENUBARBREAK
)
2591 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2592 DPRINT("ret %d.\n", i
);
2597 /***********************************************************************
2600 * Return the handle of the selected sub-popup menu (if any).
2602 static HMENU FASTCALL
2603 MenuGetSubPopup(HMENU Menu
)
2605 ROSMENUINFO MenuInfo
;
2606 ROSMENUITEMINFO ItemInfo
;
2608 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2609 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2614 MenuInitRosMenuItemInfo(&ItemInfo
);
2615 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2617 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2620 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2622 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2623 return ItemInfo
.hSubMenu
;
2626 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2630 /***********************************************************************
2633 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2635 static LRESULT FASTCALL
2636 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2638 ROSMENUINFO TopMenuInfo
;
2639 ROSMENUINFO MenuInfo
;
2641 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2643 return (LRESULT
) FALSE
;
2646 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2647 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2649 MDINEXTMENU NextMenu
;
2654 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2655 NextMenu
.hmenuNext
= NULL
;
2656 NextMenu
.hwndNext
= NULL
;
2657 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2659 DPRINT("%p [%p] -> %p [%p]\n",
2660 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2662 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2664 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2665 NewWnd
= Mt
->OwnerWnd
;
2666 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2668 /* switch to the menu bar */
2670 if (0 != (Style
& WS_CHILD
)
2671 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2678 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2682 Id
= MenuInfo
.MenuItemCount
- 1;
2685 else if (0 != (Style
& WS_SYSMENU
))
2687 /* switch to the system menu */
2688 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2695 else /* application returned a new menu to switch to */
2697 NewMenu
= NextMenu
.hmenuNext
;
2698 NewWnd
= NextMenu
.hwndNext
;
2700 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2702 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2704 if (0 != (Style
& WS_SYSMENU
)
2705 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2707 /* get the real system menu */
2708 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2710 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2712 /* FIXME: Not sure what to do here;
2713 * perhaps try to track NewMenu as a popup? */
2715 DPRINT(" -- got confused.\n");
2725 if (NewMenu
!= Mt
->TopMenu
)
2727 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2729 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2731 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2735 if (NewWnd
!= Mt
->OwnerWnd
)
2737 Mt
->OwnerWnd
= NewWnd
;
2738 SetCapture(Mt
->OwnerWnd
);
2739 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2742 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2743 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2745 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2754 /***********************************************************************
2757 * The idea is not to show the popup if the next input message is
2758 * going to hide it anyway.
2760 static BOOL FASTCALL
2761 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2765 Msg
.hwnd
= Mt
->OwnerWnd
;
2767 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2768 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2773 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2774 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2776 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2777 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2778 if (WM_KEYDOWN
== Msg
.message
2779 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2781 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2788 /* failures go through this */
2789 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2794 /***********************************************************************
2797 * Handle a VK_ESCAPE key event in a menu.
2799 static BOOL FASTCALL
2800 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2802 BOOL EndMenu
= TRUE
;
2803 ROSMENUINFO MenuInfo
;
2804 HMENU MenuTmp
, MenuPrev
;
2806 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2808 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2809 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2811 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2813 /* close topmost popup */
2814 while (MenuTmp
!= Mt
->CurrentMenu
)
2817 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2820 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2822 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2824 Mt
->CurrentMenu
= MenuPrev
;
2832 /***********************************************************************
2835 * Handle a VK_LEFT key event in a menu.
2837 static void FASTCALL
2838 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2840 ROSMENUINFO MenuInfo
;
2841 ROSMENUINFO TopMenuInfo
;
2842 ROSMENUINFO PrevMenuInfo
;
2843 HMENU MenuTmp
, MenuPrev
;
2846 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2848 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2853 /* Try to move 1 column left (if possible) */
2854 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2856 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2858 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2863 /* close topmost popup */
2864 while (MenuTmp
!= Mt
->CurrentMenu
)
2867 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2870 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
2874 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
2875 Mt
->CurrentMenu
= MenuPrev
;
2877 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2881 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
2883 /* move menu bar selection if no more popups are left */
2885 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
2887 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
2890 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2892 /* A sublevel menu was displayed - display the next one
2893 * unless there is another displacement coming up */
2895 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2896 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2898 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
2905 /***********************************************************************
2908 * Handle a VK_RIGHT key event in a menu.
2910 static void FASTCALL
2911 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
2914 ROSMENUINFO MenuInfo
;
2915 ROSMENUINFO CurrentMenuInfo
;
2918 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2919 Mt
->CurrentMenu
, Mt
->TopMenu
);
2921 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2925 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
2927 /* If already displaying a popup, try to display sub-popup */
2929 MenuTmp
= Mt
->CurrentMenu
;
2930 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2932 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
2935 /* if subpopup was displayed then we are done */
2936 if (MenuTmp
!= Mt
->CurrentMenu
)
2942 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2947 /* Check to see if there's another column */
2948 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
2950 DPRINT("Going to %d.\n", NextCol
);
2951 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2953 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
2958 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
2960 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2962 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
2963 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
2970 /* try to move to the next item */
2971 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
2973 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
2976 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2978 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2979 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2981 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
2988 /***********************************************************************
2991 * Find the menu item selected by a key press.
2992 * Return item id, -1 if none, -2 if we should close the menu.
2994 static UINT FASTCALL
2995 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
2996 WCHAR Key
, BOOL ForceMenuChar
)
2998 ROSMENUINFO SysMenuInfo
;
2999 PROSMENUITEMINFO Items
, ItemInfo
;
3003 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
3005 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
3007 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
3009 MenuInfo
= &SysMenuInfo
;
3017 if (NULL
!= MenuInfo
)
3019 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
3023 if (! ForceMenuChar
)
3025 Key
= towupper(Key
);
3027 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
3029 if (IS_STRING_ITEM(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
3031 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
3034 p
= wcschr(p
+ 2, '&');
3036 while (NULL
!= p
&& L
'&' == p
[1]);
3037 if (NULL
!= p
&& (towupper(p
[1]) == Key
))
3045 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3046 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3047 if (2 == HIWORD(MenuChar
))
3049 return LOWORD(MenuChar
);
3051 if (1 == HIWORD(MenuChar
))
3060 /***********************************************************************
3063 * Menu tracking code.
3066 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3067 HWND Wnd
, const RECT
*Rect
)
3070 ROSMENUINFO MenuInfo
;
3071 ROSMENUITEMINFO ItemInfo
;
3073 INT ExecutedMenuId
= -1;
3075 BOOL EnterIdleSent
= FALSE
;
3078 Mt
.CurrentMenu
= Menu
;
3084 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3085 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3086 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3089 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3094 if (0 != (Flags
& TPM_BUTTONDOWN
))
3096 /* Get the result in order to start the tracking or not */
3097 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3098 fEndMenu
= ! fRemove
;
3101 SetCapture(Mt
.OwnerWnd
);
3102 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3106 /* we have to keep the message in the queue until it's
3107 * clear that menu loop is not over yet. */
3111 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3113 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3117 /* remove the message from the queue */
3118 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3122 if (! EnterIdleSent
)
3124 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3125 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3126 EnterIdleSent
= TRUE
;
3127 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3133 /* check if EndMenu() tried to cancel us, by posting this message */
3134 if (WM_CANCELMODE
== Msg
.message
)
3136 /* we are now out of the loop */
3139 /* remove the message from the queue */
3140 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3142 /* break out of internal loop, ala ESCAPE */
3146 TranslateMessage(&Msg
);
3149 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3151 EnterIdleSent
= FALSE
;
3155 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3158 * Use the mouse coordinates in lParam instead of those in the MSG
3159 * struct to properly handle synthetic messages. They are already
3160 * in screen coordinates.
3162 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3163 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3165 /* Find a menu for this mouse event */
3166 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3170 /* no WM_NC... messages in captured state */
3172 case WM_RBUTTONDBLCLK
:
3173 case WM_RBUTTONDOWN
:
3174 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3179 case WM_LBUTTONDBLCLK
:
3180 case WM_LBUTTONDOWN
:
3181 /* If the message belongs to the menu, removes it from the queue */
3182 /* Else, end menu tracking */
3183 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3184 fEndMenu
= ! fRemove
;
3188 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3194 /* Check if a menu was selected by the mouse */
3197 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3199 /* End the loop if ExecutedMenuId is an item ID */
3200 /* or if the job was done (ExecutedMenuId = 0). */
3201 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3205 /* No menu was selected by the mouse */
3206 /* if the function was called by TrackPopupMenu, continue
3207 with the menu tracking. If not, stop it */
3208 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3215 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3219 } /* switch(Msg.message) - mouse */
3221 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3223 fRemove
= TRUE
; /* Keyboard messages are always removed */
3231 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3233 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3239 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3241 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3242 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3246 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3247 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3249 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3251 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3253 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3257 else /* otherwise try to move selection */
3259 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3265 MenuKeyLeft(&Mt
, Flags
);
3269 MenuKeyRight(&Mt
, Flags
);
3273 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3279 hi
.cbSize
= sizeof(HELPINFO
);
3280 hi
.iContextType
= HELPINFO_MENUITEM
;
3281 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3283 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3289 MenuInitRosMenuItemInfo(&ItemInfo
);
3290 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3291 MenuInfo
.FocusedItem
,
3294 hi
.iCtrlId
= ItemInfo
.wID
;
3300 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3303 hi
.hItemHandle
= Menu
;
3304 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3305 hi
.MousePos
= Msg
.pt
;
3306 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3313 break; /* WM_KEYDOWN */
3322 break; /* WM_SYSKEYDOWN */
3328 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3332 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3334 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3335 fEndMenu
= (ExecutedMenuId
!= -1);
3339 /* Hack to avoid control chars. */
3340 /* We will find a better way real soon... */
3341 if (Msg
.wParam
< 32)
3346 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3347 LOWORD(Msg
.wParam
), FALSE
);
3348 if ((UINT
) -2 == Pos
)
3352 else if ((UINT
) -1 == Pos
)
3358 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3359 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3360 fEndMenu
= (-1 != ExecutedMenuId
);
3364 } /* switch(msg.message) - kbd */
3368 DispatchMessageW(&Msg
);
3376 /* finally remove message from the queue */
3378 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3380 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3384 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3388 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3389 SetCapture(NULL
); /* release the capture */
3391 /* If dropdown is still painted and the close box is clicked on
3392 then the menu will be destroyed as part of the DispatchMessage above.
3393 This will then invalidate the menu handle in Mt.hTopMenu. We should
3394 check for this first. */
3395 if (IsMenu(Mt
.TopMenu
))
3397 if (IsWindow(Mt
.OwnerWnd
))
3399 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3401 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3403 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3405 DestroyWindow(MenuInfo
.Wnd
);
3406 MenuInfo
.Wnd
= NULL
;
3408 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3411 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3414 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3416 /* Reset the variable for hiding menu */
3417 MenuInfo
.TimeToHide
= FALSE
;
3418 MenuSetRosMenuInfo(&MenuInfo
);
3422 /* The return value is only used by TrackPopupMenu */
3423 return (-1 != ExecutedMenuId
) ? ExecutedMenuId
: 0;
3426 /***********************************************************************
3429 static BOOL FASTCALL
3430 MenuExitTracking(HWND Wnd
)
3432 DPRINT("hwnd=%p\n", Wnd
);
3434 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3441 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3443 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3444 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3446 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3450 /* map point to parent client coordinates */
3451 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3452 if (Parent
!= GetDesktopWindow())
3454 ScreenToClient(Parent
, &Pt
);
3457 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3458 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3459 MenuExitTracking(Wnd
);
3465 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
3469 /* FUNCTIONS *****************************************************************/
3472 MenuIsStringItem(ULONG TypeData)
3474 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3482 AppendMenuA(HMENU hMenu
,
3484 UINT_PTR uIDNewItem
,
3487 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3496 AppendMenuW(HMENU hMenu
,
3498 UINT_PTR uIDNewItem
,
3501 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3510 CheckMenuItem(HMENU hmenu
,
3514 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3522 CheckMenuRadioItem(HMENU hmenu
,
3540 return NtUserCreateMenu(FALSE
);
3548 CreatePopupMenu(VOID
)
3551 return NtUserCreateMenu(TRUE
);
3559 DeleteMenu(HMENU hMenu
,
3563 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
3571 DestroyMenu(HMENU hMenu
)
3573 return NtUserDestroyMenu(hMenu
);
3581 DrawMenuBar(HWND hWnd
)
3584 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
3593 EnableMenuItem(HMENU hMenu
,
3597 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
3607 guii
.cbSize
= sizeof(GUITHREADINFO
);
3608 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
3610 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
3622 return NtUserGetMenu(hWnd
);
3630 GetMenuBarInfo(HWND hwnd
,
3635 return (BOOL
)NtUserGetMenuBarInfo(hwnd
, idObject
, idItem
, pmbi
);
3643 GetMenuCheckMarkDimensions(VOID
)
3645 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
3646 GetSystemMetrics(SM_CYMENUCHECK
)));
3654 GetMenuDefaultItem(HMENU hMenu
,
3658 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
3666 GetMenuInfo(HMENU hmenu
,
3672 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
3675 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
3676 mi
.cbSize
= sizeof(MENUINFO
);
3677 mi
.fMask
= lpcmi
->fMask
;
3679 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
3681 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
3690 GetMenuItemCount(HMENU Menu
)
3692 ROSMENUINFO MenuInfo
;
3694 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
3702 GetMenuItemID(HMENU hMenu
,
3705 ROSMENUITEMINFO mii
;
3707 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3708 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
3710 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
3715 if (NULL
!= mii
.hSubMenu
)
3736 LPMENUITEMINFOA mii
)
3741 if (0 == (mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)))
3743 /* No text requested, just pass on */
3744 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3747 AString
= mii
->dwTypeData
;
3749 mii
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0, mii
->cch
* sizeof(WCHAR
));
3750 if (NULL
== mii
->dwTypeData
)
3752 mii
->dwTypeData
= AString
;
3755 if (! NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
))
3757 HeapFree(GetProcessHeap(), 0, mii
->dwTypeData
);
3758 mii
->dwTypeData
= AString
;
3762 if (IS_STRING_ITEM(mii
->fType
))
3764 WideCharToMultiByte(CP_ACP
, 0, (LPWSTR
) mii
->dwTypeData
, mii
->cch
, AString
, ACch
,
3767 HeapFree(GetProcessHeap(), 0, mii
->dwTypeData
);
3768 mii
->dwTypeData
= AString
;
3783 LPMENUITEMINFOW mii
)
3785 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3793 GetMenuItemRect(HWND hWnd
,
3813 ROSMENUINFO MenuInfo
;
3814 ROSMENUITEMINFO mii
;
3816 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3817 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
3820 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
3825 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
3829 nSubItems
= MenuInfo
.MenuItemCount
;
3831 /* FIXME - ported from wine, does that work (0xff)? */
3832 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
3833 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
3835 return (UINT
)-1; /* Invalid submenu */
3838 /* FIXME - ported from wine, does that work? */
3839 return (mii
.fType
| mii
.fState
);
3891 mi
.cbSize
= sizeof(MENUITEMINFOW
);
3892 mi
.fMask
= MIIM_SUBMENU
;
3894 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
3896 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
3913 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
3915 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
3930 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
3943 UINT_PTR uIDNewItem
,
3947 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3948 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
3950 mii
.fState
= MFS_ENABLED
;
3952 if(uFlags
& MF_BITMAP
)
3954 mii
.fType
|= MFT_BITMAP
;
3955 mii
.fMask
|= MIIM_BITMAP
;
3956 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
3958 else if(uFlags
& MF_OWNERDRAW
)
3960 mii
.fType
|= MFT_OWNERDRAW
;
3961 mii
.fMask
|= MIIM_DATA
;
3962 mii
.dwItemData
= (DWORD
) lpNewItem
;
3966 mii
.fMask
|= MIIM_TYPE
;
3967 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
3968 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
3971 if(uFlags
& MF_RIGHTJUSTIFY
)
3973 mii
.fType
|= MFT_RIGHTJUSTIFY
;
3975 if(uFlags
& MF_MENUBREAK
)
3977 mii
.fType
|= MFT_MENUBREAK
;
3979 if(uFlags
& MF_MENUBARBREAK
)
3981 mii
.fType
|= MFT_MENUBARBREAK
;
3983 if(uFlags
& MF_DISABLED
)
3985 mii
.fState
|= MFS_DISABLED
;
3987 if(uFlags
& MF_GRAYED
)
3989 mii
.fState
|= MFS_GRAYED
;
3992 if(uFlags
& MF_POPUP
)
3994 mii
.fType
|= MF_POPUP
;
3995 mii
.fMask
|= MIIM_SUBMENU
;
3996 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4000 mii
.fMask
|= MIIM_ID
;
4001 mii
.wID
= (UINT
)uIDNewItem
;
4003 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4016 LPCMENUITEMINFOA lpmii
)
4019 UNICODE_STRING MenuText
;
4021 BOOL CleanHeap
= FALSE
;
4024 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4025 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4027 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4029 /* copy the text string */
4030 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4031 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4033 Status
= HEAP_strdupAtoW ( &mi
.dwTypeData
, (LPCSTR
)mi
.dwTypeData
, &mi
.cch
);
4034 if (!NT_SUCCESS (Status
))
4036 SetLastError (RtlNtStatusToDosError(Status
));
4039 RtlInitUnicodeString(&MenuText
, (PWSTR
)mi
.dwTypeData
);
4040 mi
.dwTypeData
= (LPWSTR
)&MenuText
;
4044 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4046 if ( CleanHeap
) HEAP_free ( mi
.dwTypeData
);
4061 LPCMENUITEMINFOW lpmii
)
4064 UNICODE_STRING MenuText
;
4066 BOOL CleanHeap
= FALSE
;
4067 HANDLE hHeap
= GetProcessHeap();
4068 mi
.hbmpItem
= (HBITMAP
)0;
4070 // while we could just pass 'lpmii' to win32k, we make a copy so that
4071 // if a bad user passes bad data, we crash his process instead of the
4074 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4075 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4077 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
4079 /* copy the text string */
4080 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4081 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4085 if(!RtlCreateUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
))
4087 SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY
));
4090 mi
.dwTypeData
= (LPWSTR
)&MenuText
;
4091 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4096 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4098 if(CleanHeap
) RtlFreeHeap (hHeap
, 0, mi
.dwTypeData
);
4113 UINT_PTR uIDNewItem
,
4117 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4118 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4120 mii
.fState
= MFS_ENABLED
;
4122 if(uFlags
& MF_BITMAP
)
4124 mii
.fType
|= MFT_BITMAP
;
4125 mii
.fMask
|= MIIM_BITMAP
;
4126 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4128 else if(uFlags
& MF_OWNERDRAW
)
4130 mii
.fType
|= MFT_OWNERDRAW
;
4131 mii
.fMask
|= MIIM_DATA
;
4132 mii
.dwItemData
= (DWORD
) lpNewItem
;
4136 mii
.fMask
|= MIIM_TYPE
;
4137 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4138 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4141 if(uFlags
& MF_RIGHTJUSTIFY
)
4143 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4145 if(uFlags
& MF_MENUBREAK
)
4147 mii
.fType
|= MFT_MENUBREAK
;
4149 if(uFlags
& MF_MENUBARBREAK
)
4151 mii
.fType
|= MFT_MENUBARBREAK
;
4153 if(uFlags
& MF_DISABLED
)
4155 mii
.fState
|= MFS_DISABLED
;
4157 if(uFlags
& MF_GRAYED
)
4159 mii
.fState
|= MFS_GRAYED
;
4162 if(uFlags
& MF_POPUP
)
4164 mii
.fType
|= MF_POPUP
;
4165 mii
.fMask
|= MIIM_SUBMENU
;
4166 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4170 mii
.fMask
|= MIIM_ID
;
4171 mii
.wID
= (UINT
)uIDNewItem
;
4173 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4185 ROSMENUINFO MenuInfo
;
4187 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4195 LoadMenuA(HINSTANCE hInstance
,
4198 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4199 if (Resource
== NULL
)
4203 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4211 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4213 return(LoadMenuIndirectW(lpMenuTemplate
));
4221 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4224 WORD version
, offset
;
4225 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4227 version
= GET_WORD(p
);
4232 case 0: /* standard format is version of 0 */
4233 offset
= GET_WORD(p
);
4234 p
+= sizeof(WORD
) + offset
;
4235 if (!(hMenu
= CreateMenu())) return 0;
4236 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4242 case 1: /* extended format is version of 1 */
4243 offset
= GET_WORD(p
);
4244 p
+= sizeof(WORD
) + offset
;
4245 if (!(hMenu
= CreateMenu())) return 0;
4246 if (!MENUEX_ParseResource(p
, hMenu
))
4248 DestroyMenu( hMenu
);
4253 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4263 LoadMenuW(HINSTANCE hInstance
,
4266 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4267 if (Resource
== NULL
)
4271 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4285 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4298 UINT_PTR uIDNewItem
,
4315 UINT_PTR uIDNewItem
,
4333 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
4344 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4358 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
4373 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4376 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4377 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4390 HBITMAP hBitmapUnchecked
,
4391 HBITMAP hBitmapChecked
)
4407 LPCMENUITEMINFOA lpmii
)
4423 LPCMENUITEMINFOW lpmii
)
4440 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4445 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4448 return NtUserSetSystemMenu(hwnd
, hMenu
);
4468 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
4470 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4471 if (0 == (Flags
& TPM_NONOTIFY
))
4473 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
4476 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
4478 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
4480 MenuExitTracking(Wnd
);
4499 /* Not fully implemented */
4500 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
4501 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
4510 SetMenuContextHelpId(HMENU hmenu
,
4511 DWORD dwContextHelpId
)
4513 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
4522 GetMenuContextHelpId(HMENU hmenu
)
4525 mi
.cbSize
= sizeof(ROSMENUINFO
);
4526 mi
.fMask
= MIM_HELPID
;
4528 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
4530 return mi
.dwContextHelpID
;
4575 LPCWSTR lpszNewItem
,
4580 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4581 for MF_DELETE. We should check the parameters for all others
4582 MF_* actions also (anybody got a doc on ChangeMenu?).
4585 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4588 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4591 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4594 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4597 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4598 flags
&~ MF_REMOVE
);
4600 default : /* MF_INSERT */
4601 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
4618 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4619 for MF_DELETE. We should check the parameters for all others
4620 MF_* actions also (anybody got a doc on ChangeMenu?).
4623 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4626 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4629 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4632 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4635 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4636 flags
&~ MF_REMOVE
);
4638 default : /* MF_INSERT */
4639 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);