3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS user32.dll
5 * FILE: user32/windows/menu.c
8 * PROGRAMMERS: Casper S. Hornstrup
12 /* INCLUDES ******************************************************************/
15 #include <wine/debug.h>
17 LRESULT
DefWndNCPaint(HWND hWnd
, HRGN hRgn
, BOOL Active
);
19 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
21 /* internal popup menu window messages */
23 #define MM_SETMENUHANDLE (WM_USER + 0)
24 #define MM_GETMENUHANDLE (WM_USER + 1)
26 /* internal flags for menu tracking */
28 #define TF_ENDMENU 0x10000
29 #define TF_SUSPENDPOPUP 0x20000
30 #define TF_SKIPREMOVE 0x40000
35 /* Internal MenuTrackMenu() flags */
36 #define TPM_INTERNAL 0xF0000000
37 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
38 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
39 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
42 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
44 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
46 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
47 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
48 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
50 #define IS_SYSTEM_MENU(MenuInfo) \
51 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
53 #define IS_SYSTEM_POPUP(MenuInfo) \
54 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
56 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
58 /* Use global popup window because there's no way 2 menus can
59 * be tracked at the same time. */
60 static HWND top_popup
;
62 /* Flag set by EndMenu() to force an exit from menu tracking */
63 static BOOL fEndMenu
= FALSE
;
65 #define MENU_ITEM_HBMP_SPACE (5)
66 #define MENU_BAR_ITEMS_SPACE (12)
67 #define SEPARATOR_HEIGHT (5)
68 #define MENU_TAB_SPACE (8)
70 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
71 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
72 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
73 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
78 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
79 HMENU TopMenu
; /* initial menu */
80 HWND OwnerWnd
; /* where notifications are sent */
85 /*********************************************************************
86 * PopupMenu class descriptor
88 const struct builtin_class_descr POPUPMENU_builtin_class
=
90 POPUPMENU_CLASS_ATOMW
, /* name */
91 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
92 (WNDPROC
) NULL
, /* FIXME - procA */
93 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
94 sizeof(MENUINFO
*), /* extra */
95 (LPCWSTR
) IDC_ARROW
, /* cursor */
96 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
100 #define GET_WORD(ptr) (*(WORD *)(ptr))
103 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
106 HFONT hMenuFont
= NULL
;
107 HFONT hMenuFontBold
= NULL
;
109 /* Dimension of the menu bitmaps */
110 static HBITMAP BmpSysMenu
= NULL
;
112 static SIZE MenuCharSize
;
114 /***********************************************************************
117 * Get full information about menu
120 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
122 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
123 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
125 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
128 /***********************************************************************
131 * Set full information about menu
134 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
136 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
137 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
139 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
142 /***********************************************************************
143 * MenuInitRosMenuItemInfo
145 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
148 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
150 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
151 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
154 /***********************************************************************
155 * MenuGetRosMenuItemInfo
157 * Get full information about a menu item
160 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
162 UINT Save_Mask
= ItemInfo
->fMask
; /* Save the org mask bits. */
164 if (ItemInfo
->dwTypeData
!= NULL
)
166 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
170 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
171 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
172 ItemInfo
->dwTypeData
= NULL
;
174 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
180 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
)
183 ItemInfo
->dwTypeData
= HeapAlloc(GetProcessHeap(), 0,
184 ItemInfo
->cch
* sizeof(WCHAR
));
185 if (NULL
== ItemInfo
->dwTypeData
)
190 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
195 ItemInfo
->dwTypeData
[ItemInfo
->cch
- 1] = UNICODE_NULL
;
197 ItemInfo
->fMask
= Save_Mask
;
201 /***********************************************************************
202 * MenuSetRosMenuItemInfo
204 * Set selected information about a menu item, need to set the mask bits.
207 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
211 if (MENU_ITEM_TYPE(ItemInfo
->fType
) == MF_STRING
&&
212 ItemInfo
->dwTypeData
!= NULL
)
214 ItemInfo
->cch
= strlenW(ItemInfo
->dwTypeData
);
216 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
221 /***********************************************************************
222 * MenuCleanupRosMenuItemInfo
224 * Cleanup after use of MenuGet/SetRosMenuItemInfo
227 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
229 if (ItemInfo
->dwTypeData
!= NULL
)
231 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
232 ItemInfo
->dwTypeData
= NULL
;
236 /***********************************************************************
237 * MenuGetAllRosMenuItemInfo
239 * Get full information about all menu items
242 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
246 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
247 if (BufSize
== (DWORD
) -1 || BufSize
== 0)
251 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
252 if (NULL
== *ItemInfo
)
257 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
260 /***********************************************************************
261 * MenuCleanupAllRosMenuItemInfo
263 * Cleanup after use of MenuGetAllRosMenuItemInfo
266 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
268 HeapFree(GetProcessHeap(), 0, ItemInfo
);
271 /***********************************************************************
272 * MenuInitSysMenuPopup
274 * Grey the appropriate items in System menu.
276 void FASTCALL
MenuInitSysMenuPopup(HMENU hmenu
, DWORD style
, DWORD clsStyle
, LONG HitTest
)
284 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
285 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
286 gray
= ((style
& WS_MAXIMIZE
) != 0);
287 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
288 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
289 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
290 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
291 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
292 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
293 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
294 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
296 /* The menu item must keep its state if it's disabled */
298 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
300 /* Set default menu item */
301 if(style
& WS_MINIMIZE
) DefItem
= SC_RESTORE
;
302 else if(HitTest
== HTCAPTION
) DefItem
= ((style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
303 else DefItem
= SC_CLOSE
;
305 mii
.cbSize
= sizeof(MENUITEMINFOW
);
306 mii
.fMask
|= MIIM_STATE
;
307 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(hmenu
, DefItem
, FALSE
, &mii
) &&
308 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
))) DefItem
= SC_CLOSE
;
310 SetMenuDefaultItem(hmenu
, DefItem
, MF_BYCOMMAND
);
313 /******************************************************************************
315 * UINT MenuGetStartOfNextColumn(
316 * PROSMENUINFO MenuInfo)
318 *****************************************************************************/
319 static UINT
MenuGetStartOfNextColumn(
320 PROSMENUINFO MenuInfo
)
322 PROSMENUITEMINFO MenuItems
;
325 i
= MenuInfo
->FocusedItem
;
326 if ( i
== NO_SELECTED_ITEM
)
329 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
330 return NO_SELECTED_ITEM
;
332 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
333 if (0 != (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
336 return NO_SELECTED_ITEM
;
339 /******************************************************************************
341 * UINT MenuGetStartOfPrevColumn(
342 * PROSMENUINFO MenuInfo)
344 *****************************************************************************/
346 static UINT FASTCALL
MenuGetStartOfPrevColumn(
347 PROSMENUINFO MenuInfo
)
349 PROSMENUITEMINFO MenuItems
;
352 if (!MenuInfo
->FocusedItem
|| MenuInfo
->FocusedItem
== NO_SELECTED_ITEM
)
353 return NO_SELECTED_ITEM
;
355 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
356 return NO_SELECTED_ITEM
;
358 /* Find the start of the column */
359 for (i
= MenuInfo
->FocusedItem
;
360 0 != i
&& 0 == (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
368 MenuCleanupAllRosMenuItemInfo(MenuItems
);
369 return NO_SELECTED_ITEM
;
372 for (--i
; 0 != i
; --i
)
373 if (MenuItems
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
376 MenuCleanupAllRosMenuItemInfo(MenuItems
);
377 TRACE("ret %d.\n", i
);
382 /***********************************************************************
385 * Find a Sub menu. Return the position of the submenu, and modifies
386 * *hmenu in case it is found in another sub-menu.
387 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
389 static UINT FASTCALL
MenuFindSubMenu(HMENU
*hmenu
, HMENU hSubTarget
)
393 ROSMENUITEMINFO item
;
394 if (((*hmenu
)==(HMENU
)0xffff) ||
395 (!MenuGetRosMenuInfo(&menu
, *hmenu
)))
396 return NO_SELECTED_ITEM
;
398 MenuInitRosMenuItemInfo(&item
);
399 for (i
= 0; i
< menu
.MenuItemCount
; i
++) {
400 if (! MenuGetRosMenuItemInfo(menu
.Self
, i
, &item
))
402 MenuCleanupRosMenuItemInfo(&item
);
403 return NO_SELECTED_ITEM
;
405 if (!(item
.fType
& MF_POPUP
)) continue;
406 if (item
.hSubMenu
== hSubTarget
) {
407 MenuCleanupRosMenuItemInfo(&item
);
411 HMENU hsubmenu
= item
.hSubMenu
;
412 UINT pos
= MenuFindSubMenu(&hsubmenu
, hSubTarget
);
413 if (pos
!= NO_SELECTED_ITEM
) {
419 MenuCleanupRosMenuItemInfo(&item
);
420 return NO_SELECTED_ITEM
;
423 /***********************************************************************
426 * Load the arrow bitmap. We can't do this from MenuInit since user32
427 * can also be used (and thus initialized) from text-mode.
430 MenuLoadBitmaps(VOID
)
432 /* Load system buttons bitmaps */
433 if (NULL
== BmpSysMenu
)
435 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
439 /***********************************************************************
442 * Draws popup magic glyphs (can be found in system menu).
445 MenuDrawPopupGlyph(HDC dc
, LPRECT r
, INT_PTR popupMagic
, BOOL inactive
, BOOL hilite
)
448 HFONT hFont
, hOldFont
;
454 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
457 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
460 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
463 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
467 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic
);
470 ZeroMemory(&lf
, sizeof(LOGFONTW
));
471 InflateRect(r
, -2, -2);
472 lf
.lfHeight
= r
->bottom
- r
->top
;
474 lf
.lfWeight
= FW_NORMAL
;
475 lf
.lfCharSet
= DEFAULT_CHARSET
;
476 lstrcpy(lf
.lfFaceName
, TEXT("Marlett"));
477 hFont
= CreateFontIndirect(&lf
);
478 /* save font and text color */
479 hOldFont
= SelectObject(dc
, hFont
);
480 clrsave
= GetTextColor(dc
);
481 bkmode
= GetBkMode(dc
);
482 /* set color and drawing mode */
483 SetBkMode(dc
, TRANSPARENT
);
489 SetTextColor(dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
490 TextOut(dc
, r
->left
+ 1, r
->top
+ 1, &symbol
, 1);
493 SetTextColor(dc
, GetSysColor(inactive
? COLOR_GRAYTEXT
: (hilite
? COLOR_HIGHLIGHTTEXT
: COLOR_MENUTEXT
)));
494 /* draw selected symbol */
495 TextOut(dc
, r
->left
, r
->top
, &symbol
, 1);
496 /* restore previous settings */
497 SetTextColor(dc
, clrsave
);
498 SelectObject(dc
, hOldFont
);
499 SetBkMode(dc
, bkmode
);
503 /***********************************************************************
506 * Find the menu item selected by a key press.
507 * Return item id, -1 if none, -2 if we should close the menu.
509 static UINT FASTCALL
MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
510 WCHAR Key
, BOOL ForceMenuChar
)
512 ROSMENUINFO SysMenuInfo
;
513 PROSMENUITEMINFO Items
, ItemInfo
;
517 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
519 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
521 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
523 MenuInfo
= &SysMenuInfo
;
531 if (NULL
!= MenuInfo
)
533 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
541 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
543 if ((ItemInfo
->Text
) && NULL
!= ItemInfo
->dwTypeData
)
545 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
548 p
= strchrW(p
+ 2, '&');
550 while (NULL
!= p
&& L
'&' == p
[1]);
551 if (NULL
!= p
&& (toupperW(p
[1]) == Key
))
559 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
560 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
561 if (2 == HIWORD(MenuChar
))
563 return LOWORD(MenuChar
);
565 if (1 == HIWORD(MenuChar
))
574 /***********************************************************************
575 * MenuGetBitmapItemSize
577 * Get the size of a bitmap item.
579 static void FASTCALL
MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem
, SIZE
*size
,
583 HBITMAP bmp
= lpitem
->hbmpItem
;
585 size
->cx
= size
->cy
= 0;
587 /* check if there is a magic menu item associated with this item */
588 if (IS_MAGIC_BITMAP(bmp
))
590 switch((INT_PTR
) bmp
)
592 case (INT_PTR
)HBMMENU_CALLBACK
:
594 MEASUREITEMSTRUCT measItem
;
595 measItem
.CtlType
= ODT_MENU
;
597 measItem
.itemID
= lpitem
->wID
;
598 measItem
.itemWidth
= lpitem
->Rect
.right
- lpitem
->Rect
.left
;
599 measItem
.itemHeight
= lpitem
->Rect
.bottom
- lpitem
->Rect
.top
;
600 measItem
.itemData
= lpitem
->dwItemData
;
601 SendMessageW( WndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
602 size
->cx
= measItem
.itemWidth
;
603 size
->cy
= measItem
.itemHeight
;
608 case (INT_PTR
) HBMMENU_SYSTEM
:
609 if (0 != lpitem
->dwItemData
)
611 bmp
= (HBITMAP
) lpitem
->dwItemData
;
615 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
616 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
617 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
618 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
619 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
620 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
621 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
622 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
623 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
624 /* FIXME: Why we need to subtract these magic values? */
625 /* to make them smaller than the menu bar? */
626 size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
627 size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
632 if (GetObjectW(bmp
, sizeof(BITMAP
), &bm
))
634 size
->cx
= bm
.bmWidth
;
635 size
->cy
= bm
.bmHeight
;
639 /***********************************************************************
642 * Draw a bitmap item.
644 static void FASTCALL
MenuDrawBitmapItem(HDC hdc
, PROSMENUITEMINFO lpitem
, const RECT
*rect
,
645 HMENU hmenu
, HWND WndOwner
, UINT odaction
, BOOL MenuBar
)
651 int w
= rect
->right
- rect
->left
;
652 int h
= rect
->bottom
- rect
->top
;
655 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
658 /* Check if there is a magic menu item associated with this item */
659 if (IS_MAGIC_BITMAP(hbmToDraw
))
665 switch ((INT_PTR
)hbmToDraw
)
667 case (INT_PTR
)HBMMENU_SYSTEM
:
668 if (lpitem
->dwTypeData
)
670 bmp
= (HBITMAP
)lpitem
->dwTypeData
;
671 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
675 if (!BmpSysMenu
) BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
677 if (! GetObjectW(bmp
, sizeof(bm
), &bm
)) return;
678 /* only use right half of the bitmap */
679 bmp_xoffset
= bm
.bmWidth
/ 2;
680 bm
.bmWidth
-= bmp_xoffset
;
683 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
684 flags
= DFCS_CAPTIONRESTORE
;
686 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
688 flags
= DFCS_CAPTIONMIN
;
690 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
692 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
694 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
695 flags
= DFCS_CAPTIONCLOSE
;
697 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
698 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
700 case (INT_PTR
)HBMMENU_CALLBACK
:
702 DRAWITEMSTRUCT drawItem
;
704 drawItem
.CtlType
= ODT_MENU
;
706 drawItem
.itemID
= lpitem
->wID
;
707 drawItem
.itemAction
= odaction
;
708 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
709 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
710 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
711 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
712 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
713 drawItem
.hwndItem
= (HWND
)hmenu
;
715 drawItem
.rcItem
= *rect
;
716 drawItem
.itemData
= lpitem
->dwItemData
;
717 /* some applications make this assumption on the DC's origin */
718 SetViewportOrgEx( hdc
, lpitem
->Rect
.left
, lpitem
->Rect
.top
, &origorg
);
719 OffsetRect( &drawItem
.rcItem
, - lpitem
->Rect
.left
, - lpitem
->Rect
.top
);
720 SendMessageW( WndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
721 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
726 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
727 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
728 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
729 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
730 MenuDrawPopupGlyph(hdc
, &r
, (INT_PTR
)hbmToDraw
, lpitem
->fState
& MF_GRAYED
, lpitem
->fState
& MF_HILITE
);
733 InflateRect(&r
, -1, -1);
734 if (0 != (lpitem
->fState
& MF_HILITE
))
736 flags
|= DFCS_PUSHED
;
738 DrawFrameControl(hdc
, &r
, DFC_CAPTION
, flags
);
742 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
745 hdcMem
= CreateCompatibleDC( hdc
);
746 SelectObject( hdcMem
, bmp
);
748 /* handle fontsize > bitmap_height */
749 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
751 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
752 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
753 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
754 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
758 /***********************************************************************
761 * Calculate the size of the menu item and store it in lpitem->rect.
763 static void FASTCALL
MenuCalcItemSize( HDC hdc
, PROSMENUITEMINFO lpitem
, PROSMENUINFO MenuInfo
, HWND hwndOwner
,
764 INT orgX
, INT orgY
, BOOL menuBar
)
767 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
770 TRACE("dc=%x owner=%x (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
772 MenuCharSize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &MenuCharSize
.cy
);
774 SetRect( &lpitem
->Rect
, orgX
, orgY
, orgX
, orgY
);
776 if (lpitem
->fType
& MF_OWNERDRAW
)
778 MEASUREITEMSTRUCT mis
;
779 mis
.CtlType
= ODT_MENU
;
781 mis
.itemID
= lpitem
->wID
;
782 mis
.itemData
= lpitem
->dwItemData
;
783 mis
.itemHeight
= HIWORD( GetDialogBaseUnits());
785 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
786 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
787 * width of a menufont character to the width of an owner-drawn menu.
789 lpitem
->Rect
.right
+= mis
.itemWidth
+ 2 * MenuCharSize
.cx
;
792 /* under at least win95 you seem to be given a standard
793 height for the menu and the height value is ignored */
794 lpitem
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
796 lpitem
->Rect
.bottom
+= mis
.itemHeight
;
798 TRACE("id=%04lx size=%dx%d\n",
799 lpitem
->wID
, mis
.itemWidth
, mis
.itemHeight
);
803 if (lpitem
->fType
& MF_SEPARATOR
)
805 lpitem
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
807 lpitem
->Rect
.right
+= check_bitmap_width
+ MenuCharSize
.cx
;
813 if (lpitem
->hbmpItem
)
818 MenuGetBitmapItemSize(lpitem
, &size
, hwndOwner
);
819 /* Keep the size of the bitmap in callback mode to be able
820 * to draw it correctly */
821 lpitem
->Rect
.right
= lpitem
->Rect
.left
+ size
.cx
;
822 if (MenuInfo
->maxBmpSize
.cx
< abs(size
.cx
) + MENU_ITEM_HBMP_SPACE
||
823 MenuInfo
->maxBmpSize
.cy
< abs(size
.cy
))
825 MenuInfo
->maxBmpSize
.cx
= abs(size
.cx
) + MENU_ITEM_HBMP_SPACE
;
826 MenuInfo
->maxBmpSize
.cy
= abs(size
.cy
);
828 MenuSetRosMenuInfo(MenuInfo
);
829 itemheight
= size
.cy
+ 2;
831 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
832 lpitem
->Rect
.right
+= 2 * check_bitmap_width
;
833 lpitem
->Rect
.right
+= 4 + MenuCharSize
.cx
;
834 lpitem
->XTab
= lpitem
->Rect
.right
;
835 lpitem
->Rect
.right
+= check_bitmap_width
;
836 } else /* hbmpItem & MenuBar */ {
837 MenuGetBitmapItemSize(lpitem
, &size
, hwndOwner
);
838 lpitem
->Rect
.right
+= size
.cx
;
839 if( lpitem
->Text
) lpitem
->Rect
.right
+= 2;
840 itemheight
= size
.cy
;
842 /* Special case: Minimize button doesn't have a space behind it. */
843 if (lpitem
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
844 lpitem
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
845 lpitem
->Rect
.right
-= 1;
849 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
850 lpitem
->Rect
.right
+= check_bitmap_width
;
851 lpitem
->Rect
.right
+= 4 + MenuCharSize
.cx
;
852 lpitem
->XTab
= lpitem
->Rect
.right
;
853 lpitem
->Rect
.right
+= check_bitmap_width
;
856 /* it must be a text item - unless it's the system menu */
857 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->Text
) {
858 HFONT hfontOld
= NULL
;
859 RECT rc
= lpitem
->Rect
;
860 LONG txtheight
, txtwidth
;
862 if ( lpitem
->fState
& MFS_DEFAULT
) {
863 hfontOld
= SelectObject( hdc
, hMenuFontBold
);
866 txtheight
= DrawTextW( hdc
, lpitem
->dwTypeData
, -1, &rc
,
867 DT_SINGLELINE
|DT_CALCRECT
);
868 lpitem
->Rect
.right
+= rc
.right
- rc
.left
;
869 itemheight
= max( max( itemheight
, txtheight
),
870 GetSystemMetrics( SM_CYMENU
) - 1);
871 lpitem
->Rect
.right
+= 2 * MenuCharSize
.cx
;
873 if ((p
= strchrW( lpitem
->dwTypeData
, '\t' )) != NULL
) {
876 int n
= (int)( p
- lpitem
->dwTypeData
);
877 /* Item contains a tab (only meaningful in popup menus) */
878 /* get text size before the tab */
879 txtheight
= DrawTextW( hdc
, lpitem
->dwTypeData
, n
, &rc
,
880 DT_SINGLELINE
|DT_CALCRECT
);
881 txtwidth
= rc
.right
- rc
.left
;
882 p
+= 1; /* advance past the Tab */
883 /* get text size after the tab */
884 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
885 DT_SINGLELINE
|DT_CALCRECT
);
886 lpitem
->XTab
+= txtwidth
;
887 txtheight
= max( txtheight
, tmpheight
);
888 txtwidth
+= MenuCharSize
.cx
+ /* space for the tab */
889 tmprc
.right
- tmprc
.left
; /* space for the short cut */
891 txtheight
= DrawTextW( hdc
, lpitem
->dwTypeData
, -1, &rc
,
892 DT_SINGLELINE
|DT_CALCRECT
);
893 txtwidth
= rc
.right
- rc
.left
;
894 lpitem
->XTab
+= txtwidth
;
896 lpitem
->Rect
.right
+= 2 + txtwidth
;
897 itemheight
= max( itemheight
,
898 max( txtheight
+ 2, MenuCharSize
.cy
+ 4));
900 if (hfontOld
) SelectObject (hdc
, hfontOld
);
901 } else if( menuBar
) {
902 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
904 lpitem
->Rect
.bottom
+= itemheight
;
905 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->Rect
.left
, lpitem
->Rect
.top
, lpitem
->Rect
.right
, lpitem
->Rect
.bottom
);
908 /***********************************************************************
909 * MenuPopupMenuCalcSize
911 * Calculate the size of a popup menu.
913 static void FASTCALL
MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
915 ROSMENUITEMINFO lpitem
;
918 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
;
920 MenuInfo
->Width
= MenuInfo
->Height
= 0;
921 if (MenuInfo
->MenuItemCount
== 0)
923 MenuSetRosMenuInfo(MenuInfo
);
928 SelectObject( hdc
, hMenuFont
);
933 MenuInfo
->maxBmpSize
.cx
= 0;
934 MenuInfo
->maxBmpSize
.cy
= 0;
936 MenuInitRosMenuItemInfo(&lpitem
);
937 while (start
< MenuInfo
->MenuItemCount
)
942 maxTab
= maxTabWidth
= 0;
944 /* Parse items until column break or end of menu */
945 for (i
= start
; i
< MenuInfo
->MenuItemCount
; i
++)
947 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &lpitem
))
949 MenuCleanupRosMenuItemInfo(&lpitem
);
950 MenuSetRosMenuInfo(MenuInfo
);
954 (lpitem
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
956 MenuCalcItemSize(hdc
, &lpitem
, MenuInfo
, WndOwner
, orgX
, orgY
, FALSE
);
957 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &lpitem
))
959 MenuCleanupRosMenuItemInfo(&lpitem
);
960 MenuSetRosMenuInfo(MenuInfo
);
963 // Not sure here,, The patch from wine removes this.
964 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
968 maxX
= max(maxX
, lpitem
.Rect
.right
);
969 orgY
= lpitem
.Rect
.bottom
;
970 if ((lpitem
.Text
) && lpitem
.XTab
)
972 maxTab
= max( maxTab
, lpitem
.XTab
);
973 maxTabWidth
= max(maxTabWidth
, lpitem
.Rect
.right
- lpitem
.XTab
);
977 /* Finish the column (set all items to the largest width found) */
978 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
981 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, start
, &lpitem
))
983 lpitem
.Rect
.right
= maxX
;
984 if ((lpitem
.Text
) && 0 != lpitem
.XTab
)
986 lpitem
.XTab
= maxTab
;
988 MenuSetRosMenuItemInfo(MenuInfo
->Self
, start
, &lpitem
);
992 MenuInfo
->Height
= max(MenuInfo
->Height
, orgY
);
995 MenuInfo
->Width
= maxX
;
997 /* space for 3d border */
998 MenuInfo
->Height
+= 2;
999 MenuInfo
->Width
+= 2;
1001 MenuCleanupRosMenuItemInfo(&lpitem
);
1002 MenuSetRosMenuInfo(MenuInfo
);
1003 ReleaseDC( 0, hdc
);
1006 /***********************************************************************
1007 * MenuMenuBarCalcSize
1009 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1010 * height is off by 1 pixel which causes lengthy window relocations when
1011 * active document window is maximized/restored.
1013 * Calculate the size of the menu bar.
1015 static void FASTCALL
MenuMenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1016 PROSMENUINFO MenuInfo
, HWND hwndOwner
)
1018 ROSMENUITEMINFO ItemInfo
;
1019 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1021 if ((lprect
== NULL
) || (MenuInfo
== NULL
)) return;
1022 if (MenuInfo
->MenuItemCount
== 0) return;
1023 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect
->left
, lprect
->top
, lprect
->right
, lprect
->bottom
);
1024 MenuInfo
->Width
= lprect
->right
- lprect
->left
;
1025 MenuInfo
->Height
= 0;
1026 maxY
= lprect
->top
+ 1;
1030 MenuInfo
->maxBmpSize
.cx
= 0;
1031 MenuInfo
->maxBmpSize
.cy
= 0;
1033 MenuInitRosMenuItemInfo(&ItemInfo
);
1034 while (start
< MenuInfo
->MenuItemCount
)
1036 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, start
, &ItemInfo
))
1038 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1041 orgX
= lprect
->left
;
1044 /* Parse items until line break or end of menu */
1045 for (i
= start
; i
< MenuInfo
->MenuItemCount
; i
++)
1047 if ((helpPos
== -1) && (ItemInfo
.fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1049 (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1051 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1052 MenuCalcItemSize(hdc
, &ItemInfo
, MenuInfo
, hwndOwner
, orgX
, orgY
, TRUE
);
1053 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1055 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1059 if (ItemInfo
.Rect
.right
> lprect
->right
)
1061 if (i
!= start
) break;
1062 else ItemInfo
.Rect
.right
= lprect
->right
;
1064 maxY
= max( maxY
, ItemInfo
.Rect
.bottom
);
1065 orgX
= ItemInfo
.Rect
.right
;
1066 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1068 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1070 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1076 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1077 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1079 /* Finish the line (set all items to the largest height found) */
1082 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, start
, &ItemInfo
))
1084 ItemInfo
.Rect
.bottom
= maxY
;
1085 MenuSetRosMenuItemInfo(MenuInfo
->Self
, start
, &ItemInfo
);
1090 start
= i
; /* This works! */
1094 lprect
->bottom
= maxY
;
1095 MenuInfo
->Height
= lprect
->bottom
- lprect
->top
;
1096 MenuSetRosMenuInfo(MenuInfo
);
1100 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1101 /* the last item (if several lines, only move the last line) */
1102 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1104 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1107 orgY
= ItemInfo
.Rect
.top
;
1108 orgX
= lprect
->right
;
1109 for (i
= MenuInfo
->MenuItemCount
- 1; helpPos
<= i
; i
--)
1115 if (ItemInfo
.Rect
.top
!= orgY
)
1117 break; /* Other line */
1119 if (orgX
<= ItemInfo
.Rect
.right
)
1121 break; /* Too far right already */
1123 ItemInfo
.Rect
.left
+= orgX
- ItemInfo
.Rect
.right
;
1124 ItemInfo
.Rect
.right
= orgX
;
1125 orgX
= ItemInfo
.Rect
.left
;
1126 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1127 if (helpPos
+ 1 <= i
&&
1128 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1130 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1136 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1139 /***********************************************************************
1142 * Draw a single menu item.
1144 static void FASTCALL
MenuDrawMenuItem(HWND hWnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC hdc
,
1145 PROSMENUITEMINFO lpitem
, UINT Height
, BOOL menuBar
, UINT odaction
)
1149 BOOL flat_menu
= FALSE
;
1151 PWND Wnd
= ValidateHwnd(hWnd
);
1156 if (lpitem
->fType
& MF_SYSMENU
)
1158 if ( (Wnd
->style
& WS_MINIMIZE
))
1160 UserGetInsideRectNC(Wnd
, &rect
);
1161 UserDrawSysMenuButton(hWnd
, hdc
, &rect
,
1162 lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
1167 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1168 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1172 if (lpitem
->fState
& MF_HILITE
)
1174 if(menuBar
&& !flat_menu
) {
1175 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1176 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1178 if (lpitem
->fState
& MF_GRAYED
)
1179 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1181 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1182 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1187 if (lpitem
->fState
& MF_GRAYED
)
1188 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1190 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1191 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1194 rect
= lpitem
->Rect
;
1196 if (lpitem
->fType
& MF_OWNERDRAW
)
1199 ** Experimentation under Windows reveals that an owner-drawn
1200 ** menu is given the rectangle which includes the space it requested
1201 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1202 ** and a popup-menu arrow. This is the value of lpitem->rect.
1203 ** Windows will leave all drawing to the application except for
1204 ** the popup-menu arrow. Windows always draws that itself, after
1205 ** the menu owner has finished drawing.
1209 dis
.CtlType
= ODT_MENU
;
1211 dis
.itemID
= lpitem
->wID
;
1212 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
1214 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1215 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
1216 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1217 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1218 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
1221 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1222 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd
,
1223 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1224 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
1226 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
1227 /* Draw the popup-menu arrow */
1228 if (lpitem
->fType
& MF_POPUP
)
1231 CopyRect(&rectTemp
, &rect
);
1232 rectTemp
.left
= rectTemp
.right
- GetSystemMetrics(SM_CXMENUCHECK
);
1233 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
1238 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1240 if (lpitem
->fState
& MF_HILITE
)
1244 InflateRect (&rect
, -1, -1);
1245 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1246 InflateRect (&rect
, 1, 1);
1247 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1252 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1254 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1258 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1260 SetBkMode( hdc
, TRANSPARENT
);
1262 /* vertical separator */
1263 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1270 rc
.bottom
= Height
- 3;
1273 oldPen
= SelectObject( hdc
, GetStockObject(DC_PEN
) );
1274 SetDCPenColor(hdc
, GetSysColor(COLOR_BTNSHADOW
));
1275 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1276 LineTo( hdc
, rc
.left
, rc
.bottom
);
1277 SelectObject( hdc
, oldPen
);
1280 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1283 /* horizontal separator */
1284 if (lpitem
->fType
& MF_SEPARATOR
)
1291 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
1294 oldPen
= SelectObject( hdc
, GetStockObject(DC_PEN
) );
1295 SetDCPenColor( hdc
, GetSysColor(COLOR_BTNSHADOW
));
1296 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1297 LineTo( hdc
, rc
.right
, rc
.top
);
1298 SelectObject( hdc
, oldPen
);
1301 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1306 /* helper lines for debugging */
1307 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1308 FrameRect(hdc
, &rect
, GetStockObject(BLACK_BRUSH
));
1309 SelectObject(hdc
, GetStockObject(DC_PEN
));
1310 SetDCPenColor(hdc
, GetSysColor(COLOR_WINDOWFRAME
));
1311 MoveToEx(hdc
, rect
.left
, (rect
.top
+ rect
.bottom
) / 2, NULL
);
1312 LineTo(hdc
, rect
.right
, (rect
.top
+ rect
.bottom
) / 2);
1318 INT y
= rect
.top
+ rect
.bottom
;
1320 int checked
= FALSE
;
1321 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1322 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1323 /* Draw the check mark
1326 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1328 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
)) {
1329 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hbmpChecked
:
1330 lpitem
->hbmpUnchecked
;
1331 if (bm
) /* we have a custom bitmap */
1333 HDC hdcMem
= CreateCompatibleDC( hdc
);
1335 SelectObject( hdcMem
, bm
);
1336 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1337 check_bitmap_width
, check_bitmap_height
,
1338 hdcMem
, 0, 0, SRCCOPY
);
1342 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1345 CopyRect(&r
, &rect
);
1346 r
.right
= r
.left
+ GetSystemMetrics(SM_CXMENUCHECK
);
1347 DrawFrameControl( hdc
, &r
, DFC_MENU
,
1348 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1349 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1353 if ( lpitem
->hbmpItem
)
1356 CopyRect(&bmpRect
, &rect
);
1357 if (!(MenuInfo
->dwStyle
& MNS_CHECKORBMP
) && !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
1358 bmpRect
.left
+= check_bitmap_width
+ 2;
1359 if (!(checked
&& (MenuInfo
->dwStyle
& MNS_CHECKORBMP
)))
1361 bmpRect
.right
= bmpRect
.left
+ MenuInfo
->maxBmpSize
.cx
;
1362 MenuDrawBitmapItem(hdc
, lpitem
, &bmpRect
, MenuInfo
->Self
, WndOwner
, odaction
, menuBar
);
1365 /* Draw the popup-menu arrow */
1366 if (lpitem
->fType
& MF_POPUP
)
1369 CopyRect(&rectTemp
, &rect
);
1370 rectTemp
.left
= rectTemp
.right
- GetSystemMetrics(SM_CXMENUCHECK
);
1371 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
1374 if( !(MenuInfo
->dwStyle
& MNS_NOCHECK
))
1375 rect
.left
+= check_bitmap_width
;
1376 rect
.right
-= check_bitmap_width
;
1378 else if( lpitem
->hbmpItem
)
1379 { /* Draw the bitmap */
1380 MenuDrawBitmapItem(hdc
, lpitem
, &rect
, MenuInfo
->Self
, WndOwner
, odaction
, menuBar
);
1383 /* process text if present */
1389 UINT uFormat
= menuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
1390 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1392 if(MenuInfo
->dwStyle
& MNS_CHECKORBMP
)
1393 rect
.left
+= max(0, MenuInfo
->maxBmpSize
.cx
- GetSystemMetrics(SM_CXMENUCHECK
));
1395 rect
.left
+= MenuInfo
->maxBmpSize
.cx
;
1397 if ( lpitem
->fState
& MFS_DEFAULT
)
1399 hfontOld
= SelectObject(hdc
, hMenuFontBold
);
1403 rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
1404 rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
1407 Text
= (PWCHAR
) lpitem
->dwTypeData
;
1410 for (i
= 0; L
'\0' != Text
[i
]; i
++)
1411 if (Text
[i
] == L
'\t' || Text
[i
] == L
'\b')
1415 if(lpitem
->fState
& MF_GRAYED
)
1417 if (!(lpitem
->fState
& MF_HILITE
) )
1419 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1420 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1421 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
1422 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1424 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1427 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
1429 /* paint the shortcut text */
1430 if (!menuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
1432 if (L
'\t' == Text
[i
])
1434 rect
.left
= lpitem
->XTab
;
1435 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1439 rect
.right
= lpitem
->XTab
;
1440 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1443 if (lpitem
->fState
& MF_GRAYED
)
1445 if (!(lpitem
->fState
& MF_HILITE
) )
1447 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1448 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1449 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
1450 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1452 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1454 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
1458 SelectObject (hdc
, hfontOld
);
1462 /***********************************************************************
1465 * Paint a popup menu.
1467 static void FASTCALL
MenuDrawPopupMenu(HWND hwnd
, HDC hdc
, HMENU hmenu
)
1469 HBRUSH hPrevBrush
= 0;
1472 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1474 GetClientRect( hwnd
, &rect
);
1476 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1477 && (SelectObject( hdc
, hMenuFont
)))
1481 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1483 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1486 BOOL flat_menu
= FALSE
;
1487 ROSMENUINFO MenuInfo
;
1488 ROSMENUITEMINFO ItemInfo
;
1490 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1492 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1494 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1496 /* draw menu items */
1497 if (MenuGetRosMenuInfo(&MenuInfo
, hmenu
) && MenuInfo
.MenuItemCount
)
1501 MenuInitRosMenuItemInfo(&ItemInfo
);
1503 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
1505 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
1507 MenuDrawMenuItem(hwnd
, &MenuInfo
, MenuInfo
.WndOwner
, hdc
, &ItemInfo
,
1508 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
1512 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1516 SelectObject( hdc
, hPrevBrush
);
1521 /***********************************************************************
1524 * Paint a menu bar. Returns the height of the menu bar.
1525 * called from [windows/nonclient.c]
1527 UINT
MenuDrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1532 HMENU hMenu
= GetMenu(hwnd
);
1534 if (! MenuGetRosMenuInfo(&lppop
, hMenu
) || lprect
== NULL
)
1536 return GetSystemMetrics(SM_CYMENU
);
1541 hfontOld
= SelectObject(hDC
, hMenuFont
);
1543 MenuMenuBarCalcSize(hDC
, lprect
, &lppop
, hwnd
);
1545 lprect
->bottom
= lprect
->top
+ lppop
.Height
;
1547 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1548 return lppop
.Height
;
1551 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1554 /***********************************************************************
1557 * Display a popup menu.
1559 static BOOL FASTCALL
MenuShowPopup(HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1560 INT x
, INT y
, INT xanchor
, INT yanchor
)
1562 ROSMENUINFO MenuInfo
;
1563 ROSMENUITEMINFO ItemInfo
;
1569 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1570 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1572 if (! MenuGetRosMenuInfo(&MenuInfo
, hmenu
)) return FALSE
;
1573 if (MenuInfo
.FocusedItem
!= NO_SELECTED_ITEM
)
1575 MenuInitRosMenuItemInfo(&ItemInfo
);
1576 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1578 ItemInfo
.fMask
|= MIIM_STATE
;
1579 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1580 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1582 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1583 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1586 /* store the owner for DrawItem */
1587 MenuInfo
.WndOwner
= hwndOwner
;
1588 MenuSetRosMenuInfo(&MenuInfo
);
1590 MenuPopupMenuCalcSize(&MenuInfo
, hwndOwner
);
1592 /* adjust popup menu pos so that it fits within the desktop */
1594 width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1595 height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1597 /* FIXME: should use item rect */
1600 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1601 info
.cbSize
= sizeof(info
);
1602 GetMonitorInfoW( monitor
, &info
);
1604 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1605 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1607 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1608 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1610 if( x
+ width
> info
.rcWork
.right
)
1612 if( xanchor
&& x
>= width
- xanchor
)
1613 x
-= width
- xanchor
;
1615 if( x
+ width
> info
.rcWork
.right
)
1616 x
= info
.rcWork
.right
- width
;
1618 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1620 if( y
+ height
> info
.rcWork
.bottom
)
1622 if( yanchor
&& y
>= height
+ yanchor
)
1623 y
-= height
+ yanchor
;
1625 if( y
+ height
> info
.rcWork
.bottom
)
1626 y
= info
.rcWork
.bottom
- height
;
1628 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1630 /* NOTE: In Windows, top menu popup is not owned. */
1631 MenuInfo
.Wnd
= CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW
, NULL
,
1632 WS_POPUP
, x
, y
, width
, height
,
1633 hwndOwner
, 0, (HINSTANCE
) GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1634 (LPVOID
) MenuInfo
.Self
);
1635 if ( !MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
)) return FALSE
;
1637 top_popup
= MenuInfo
.Wnd
;
1640 /* Display the window */
1642 SetWindowPos( MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1643 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1644 UpdateWindow( MenuInfo
.Wnd
);
1648 /***********************************************************************
1651 static void FASTCALL
MenuSelectItem(HWND hwndOwner
, PROSMENUINFO hmenu
, UINT wIndex
,
1652 BOOL sendMenuSelect
, HMENU topmenu
)
1654 ROSMENUITEMINFO ItemInfo
;
1655 ROSMENUINFO TopMenuInfo
;
1658 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1660 if (!hmenu
|| !hmenu
->MenuItemCount
|| !hmenu
->Wnd
) return;
1661 if (hmenu
->FocusedItem
== wIndex
) return;
1662 if (hmenu
->Flags
& MF_POPUP
) hdc
= GetDC(hmenu
->Wnd
);
1663 else hdc
= GetDCEx(hmenu
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1665 top_popup
= hmenu
->Wnd
;
1668 SelectObject( hdc
, hMenuFont
);
1670 MenuInitRosMenuItemInfo(&ItemInfo
);
1672 /* Clear previous highlighted item */
1673 if (hmenu
->FocusedItem
!= NO_SELECTED_ITEM
)
1675 if (MenuGetRosMenuItemInfo(hmenu
->Self
, hmenu
->FocusedItem
, &ItemInfo
))
1677 ItemInfo
.fMask
|= MIIM_STATE
;
1678 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1679 MenuSetRosMenuItemInfo(hmenu
->Self
, hmenu
->FocusedItem
, &ItemInfo
);
1681 MenuDrawMenuItem(hmenu
->Wnd
, hmenu
, hwndOwner
, hdc
, &ItemInfo
,
1682 hmenu
->Height
, ! (hmenu
->Flags
& MF_POPUP
),
1686 /* Highlight new item (if any) */
1687 hmenu
->FocusedItem
= wIndex
;
1688 MenuSetRosMenuInfo(hmenu
);
1689 if (hmenu
->FocusedItem
!= NO_SELECTED_ITEM
)
1691 if (MenuGetRosMenuItemInfo(hmenu
->Self
, hmenu
->FocusedItem
, &ItemInfo
))
1693 if (!(ItemInfo
.fType
& MF_SEPARATOR
))
1695 ItemInfo
.fMask
|= MIIM_STATE
;
1696 ItemInfo
.fState
|= MF_HILITE
;
1697 MenuSetRosMenuItemInfo(hmenu
->Self
, hmenu
->FocusedItem
, &ItemInfo
);
1698 MenuDrawMenuItem(hmenu
->Wnd
, hmenu
, hwndOwner
, hdc
,
1699 &ItemInfo
, hmenu
->Height
, ! (hmenu
->Flags
& MF_POPUP
),
1704 SendMessageW(hwndOwner
, WM_MENUSELECT
,
1705 MAKELONG(ItemInfo
.fType
& MF_POPUP
? wIndex
: ItemInfo
.wID
,
1706 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1707 (hmenu
->Flags
& MF_SYSMENU
)), (LPARAM
) hmenu
->Self
);
1711 else if (sendMenuSelect
) {
1714 pos
= MenuFindSubMenu(&topmenu
, hmenu
->Self
);
1715 if (pos
!= NO_SELECTED_ITEM
)
1717 if (MenuGetRosMenuInfo(&TopMenuInfo
, topmenu
)
1718 && MenuGetRosMenuItemInfo(topmenu
, pos
, &ItemInfo
))
1720 SendMessageW(hwndOwner
, WM_MENUSELECT
,
1721 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1723 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1729 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1730 ReleaseDC(hmenu
->Wnd
, hdc
);
1733 /***********************************************************************
1736 * Moves currently selected item according to the Offset parameter.
1737 * If there is no selection then it should select the last item if
1738 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1740 static void FASTCALL
1741 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1744 ROSMENUITEMINFO ItemInfo
;
1747 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1749 /* Prevent looping */
1750 if (0 == MenuInfo
->MenuItemCount
|| 0 == Offset
)
1752 else if (Offset
< -1)
1754 else if (Offset
> 1)
1757 MenuInitRosMenuItemInfo(&ItemInfo
);
1759 OrigPos
= MenuInfo
->FocusedItem
;
1760 if (OrigPos
== NO_SELECTED_ITEM
) /* NO_SELECTED_ITEM is not -1 ! */
1767 i
= MenuInfo
->FocusedItem
;
1774 /* Clip and wrap around */
1777 i
= MenuInfo
->MenuItemCount
- 1;
1779 else if (i
>= MenuInfo
->MenuItemCount
)
1783 /* If this is a good candidate; */
1784 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1785 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1787 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1788 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1791 } while (i
!= OrigPos
);
1794 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1797 LRESULT WINAPI
PopupMenuWndProcA(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
1799 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
1805 CREATESTRUCTA
*cs
= (CREATESTRUCTA
*) lParam
;
1806 SetWindowLongPtrA(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
1810 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
1811 return MA_NOACTIVATE
;
1816 BeginPaint(Wnd
, &ps
);
1817 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongPtrA(Wnd
, 0));
1822 case WM_PRINTCLIENT
:
1824 MenuDrawPopupMenu( Wnd
, (HDC
)wParam
,
1825 (HMENU
)GetWindowLongPtrW( Wnd
, 0 ) );
1833 /* zero out global pointer in case resident popup window was destroyed. */
1834 if (Wnd
== top_popup
)
1843 if (0 == GetWindowLongPtrA(Wnd
, 0))
1845 OutputDebugStringA("no menu to display\n");
1850 SetWindowLongPtrA(Wnd
, 0, 0);
1854 case MM_SETMENUHANDLE
:
1855 SetWindowLongPtrA(Wnd
, 0, wParam
);
1858 case MM_GETMENUHANDLE
:
1860 return GetWindowLongPtrA(Wnd
, 0);
1863 return DefWindowProcA(Wnd
, Message
, wParam
, lParam
);
1869 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
1871 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
1877 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
1878 SetWindowLongPtrW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
1882 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
1883 return MA_NOACTIVATE
;
1888 BeginPaint(Wnd
, &ps
);
1889 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongPtrW(Wnd
, 0));
1894 case WM_PRINTCLIENT
:
1896 MenuDrawPopupMenu( Wnd
, (HDC
)wParam
,
1897 (HMENU
)GetWindowLongPtrW( Wnd
, 0 ) );
1905 /* zero out global pointer in case resident popup window was destroyed. */
1906 if (Wnd
== top_popup
)
1915 if (0 == GetWindowLongPtrW(Wnd
, 0))
1917 OutputDebugStringA("no menu to display\n");
1922 SetWindowLongPtrW(Wnd
, 0, 0);
1926 case MM_SETMENUHANDLE
:
1927 SetWindowLongPtrW(Wnd
, 0, wParam
);
1930 case MM_GETMENUHANDLE
:
1932 return GetWindowLongPtrW(Wnd
, 0);
1935 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
1941 /**********************************************************************
1942 * MENU_ParseResource
1944 * Parse a standard menu resource and add items to the menu.
1945 * Return a pointer to the end of the resource.
1947 * NOTE: flags is equivalent to the mtOption field
1949 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1958 flags
= GET_WORD(res
);
1960 /* remove MF_END flag before passing it to AppendMenu()! */
1961 end
= (flags
& MF_END
);
1962 if(end
) flags
^= MF_END
;
1964 res
+= sizeof(WORD
);
1965 if(!(flags
& MF_POPUP
))
1968 res
+= sizeof(WORD
);
1972 res
+= strlen(str
) + 1;
1974 res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1975 if (flags
& MF_POPUP
)
1977 hSubMenu
= CreatePopupMenu();
1978 if(!hSubMenu
) return NULL
;
1979 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1982 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1984 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1986 else /* Not a popup */
1991 flags
= MF_SEPARATOR
;
1995 if (*(LPCWSTR
)str
== 0)
1996 flags
= MF_SEPARATOR
;
1999 if (flags
& MF_SEPARATOR
)
2001 if (!(flags
& (MF_GRAYED
| MF_DISABLED
)))
2002 flags
|= MF_GRAYED
| MF_DISABLED
;
2006 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
2008 AppendMenuW(hMenu
, flags
, id
,
2009 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2016 /**********************************************************************
2017 * MENUEX_ParseResource
2019 * Parse an extended menu resource and add items to the menu.
2020 * Return a pointer to the end of the resource.
2022 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2028 mii
.cbSize
= sizeof(mii
);
2029 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
2030 mii
.fType
= GET_DWORD(res
);
2031 res
+= sizeof(DWORD
);
2032 mii
.fState
= GET_DWORD(res
);
2033 res
+= sizeof(DWORD
);
2034 mii
.wID
= GET_DWORD(res
);
2035 res
+= sizeof(DWORD
);
2036 resinfo
= GET_WORD(res
);
2037 res
+= sizeof(WORD
);
2038 /* Align the text on a word boundary. */
2039 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2040 mii
.dwTypeData
= (LPWSTR
) res
;
2041 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2042 /* Align the following fields on a dword boundary. */
2043 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2045 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2046 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, mii
.dwTypeData
);
2048 if (resinfo
& 1) { /* Pop-up? */
2049 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2050 res
+= sizeof(DWORD
);
2051 mii
.hSubMenu
= CreatePopupMenu();
2054 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2055 DestroyMenu(mii
.hSubMenu
);
2058 mii
.fMask
|= MIIM_SUBMENU
;
2059 mii
.fType
|= MF_POPUP
;
2060 mii
.wID
= (UINT
) mii
.hSubMenu
;
2062 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2064 mii
.fType
|= MF_SEPARATOR
;
2066 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2067 } while (!(resinfo
& MF_END
));
2072 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
2074 HMENU hmenu
= LoadMenuW(User32Instance
, L
"SYSMENU");
2075 LRESULT Result
= (LRESULT
)hmenu
;
2076 MENUINFO menuinfo
= {0};
2077 MENUITEMINFOW info
= {0};
2079 // removing space for checkboxes from menu
2080 menuinfo
.cbSize
= sizeof(menuinfo
);
2081 menuinfo
.fMask
= MIM_STYLE
;
2082 GetMenuInfo(hmenu
, &menuinfo
);
2083 menuinfo
.dwStyle
|= MNS_NOCHECK
;
2084 SetMenuInfo(hmenu
, &menuinfo
);
2086 // adding bitmaps to menu items
2087 info
.cbSize
= sizeof(info
);
2088 info
.fMask
|= MIIM_BITMAP
;
2089 info
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
2090 SetMenuItemInfoW(hmenu
, SC_MINIMIZE
, FALSE
, &info
);
2091 info
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
2092 SetMenuItemInfoW(hmenu
, SC_RESTORE
, FALSE
, &info
);
2093 info
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
2094 SetMenuItemInfoW(hmenu
, SC_MAXIMIZE
, FALSE
, &info
);
2095 info
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
2096 SetMenuItemInfoW(hmenu
, SC_CLOSE
, FALSE
, &info
);
2098 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
2105 NONCLIENTMETRICSW ncm
;
2107 /* get the menu font */
2108 if(!hMenuFont
|| !hMenuFontBold
)
2110 ncm
.cbSize
= sizeof(ncm
);
2111 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
2113 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2117 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
2118 if(hMenuFont
== NULL
)
2120 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2124 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
2125 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
2126 if(hMenuFontBold
== NULL
)
2128 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2129 DeleteObject(hMenuFont
);
2143 DeleteObject(hMenuFont
);
2149 DeleteObject(hMenuFontBold
);
2150 hMenuFontBold
= NULL
;
2154 /***********************************************************************
2155 * DrawMenuBarTemp (USER32.@)
2159 * called by W98SE desk.cpl Control Panel Applet
2161 * Not 100% sure about the param names, but close.
2166 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
2168 ROSMENUINFO MenuInfo
;
2169 ROSMENUITEMINFO ItemInfo
;
2171 HFONT FontOld
= NULL
;
2172 BOOL flat_menu
= FALSE
;
2174 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2178 Menu
= GetMenu(Wnd
);
2186 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2188 return GetSystemMetrics(SM_CYMENU
);
2191 TRACE("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
2193 FontOld
= SelectObject(DC
, Font
);
2195 if (0 == MenuInfo
.Height
)
2197 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
2200 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
2202 FillRect(DC
, Rect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
));
2204 SelectObject(DC
, GetStockObject(DC_PEN
));
2205 SetDCPenColor(DC
, GetSysColor(COLOR_3DFACE
));
2206 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
2207 LineTo(DC
, Rect
->right
, Rect
->bottom
);
2209 if (0 == MenuInfo
.MenuItemCount
)
2211 SelectObject(DC
, FontOld
);
2212 return GetSystemMetrics(SM_CYMENU
);
2215 MenuInitRosMenuItemInfo(&ItemInfo
);
2216 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
2218 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
2220 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
2221 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
2224 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2226 SelectObject(DC
, FontOld
);
2228 return MenuInfo
.Height
;
2231 /***********************************************************************
2234 * Display the sub-menu of the selected item of this menu.
2235 * Return the handle of the submenu, or menu if no submenu to display.
2237 static HMENU FASTCALL
2238 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2240 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2242 ROSMENUITEMINFO ItemInfo
;
2243 ROSMENUINFO SubMenuInfo
;
2247 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2249 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2251 return MenuInfo
->Self
;
2254 MenuInitRosMenuItemInfo(&ItemInfo
);
2255 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2257 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2258 return MenuInfo
->Self
;
2260 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2262 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2263 return MenuInfo
->Self
;
2266 /* message must be sent before using item,
2267 because nearly everything may be changed by the application ! */
2269 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2270 if (0 == (Flags
& TPM_NONOTIFY
))
2272 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2273 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2276 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2278 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2279 return MenuInfo
->Self
;
2281 Rect
= ItemInfo
.Rect
;
2283 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2284 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2286 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2288 Dc
= GetDC(MenuInfo
->Wnd
);
2292 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2295 SelectObject(Dc
, hMenuFont
);
2296 ItemInfo
.fMask
|= MIIM_STATE
;
2297 ItemInfo
.fState
|= MF_HILITE
;
2298 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2299 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2300 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2301 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2304 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2305 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2307 ItemInfo
.Rect
= Rect
;
2310 ItemInfo
.fMask
|= MIIM_STATE
;
2311 ItemInfo
.fState
|= MF_MOUSESELECT
;
2312 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2314 if (IS_SYSTEM_MENU(MenuInfo
))
2316 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongPtrW(MenuInfo
->Wnd
, GWL_STYLE
),
2317 GetClassLongPtrW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2319 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2320 Rect
.top
= Rect
.bottom
;
2321 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2322 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2326 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2327 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2329 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2330 Rect
.top
+= ItemInfo
.Rect
.top
- 3;
2331 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2332 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
- 3 - 2
2333 - GetSystemMetrics(SM_CYBORDER
);
2337 Rect
.left
+= ItemInfo
.Rect
.left
;
2338 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2339 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2340 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2344 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
, Flags
,
2345 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2346 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2348 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2351 Ret
= ItemInfo
.hSubMenu
;
2352 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2357 /***********************************************************************
2360 * Hide the sub-popup menus of this menu.
2362 static void FASTCALL
2363 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2365 ROSMENUINFO SubMenuInfo
;
2366 ROSMENUITEMINFO ItemInfo
;
2368 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2370 if (NULL
!= MenuInfo
&& NULL
!= top_popup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2372 MenuInitRosMenuItemInfo(&ItemInfo
);
2373 ItemInfo
.fMask
|= MIIM_FTYPE
| MIIM_STATE
;
2374 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2375 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2376 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2378 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2381 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2382 ItemInfo
.fMask
|= MIIM_STATE
;
2383 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2384 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2386 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2387 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2388 DestroyWindow(SubMenuInfo
.Wnd
);
2389 SubMenuInfo
.Wnd
= NULL
;
2390 MenuSetRosMenuInfo(&SubMenuInfo
);
2395 /***********************************************************************
2396 * MenuSwitchTracking
2398 * Helper function for menu navigation routines.
2400 static void FASTCALL
2401 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2403 ROSMENUINFO TopMenuInfo
;
2405 TRACE("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2407 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2408 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2409 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2411 /* both are top level menus (system and menu-bar) */
2412 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2413 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2414 Mt
->TopMenu
= PtMenuInfo
->Self
;
2418 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2421 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2424 /***********************************************************************
2425 * MenuExecFocusedItem
2427 * Execute a menu item (for instance when user pressed Enter).
2428 * Return the wID of the executed item. Otherwise, -1 indicating
2429 * that no menu item was executed, -2 if a popup is shown;
2430 * Have to receive the flags for the TrackPopupMenu options to avoid
2431 * sending unwanted message.
2435 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2437 ROSMENUITEMINFO ItemInfo
;
2440 TRACE("%p menu=%p\n", Mt
, MenuInfo
);
2442 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2447 MenuInitRosMenuItemInfo(&ItemInfo
);
2448 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2450 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2454 TRACE("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2456 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2458 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2459 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2461 /* If TPM_RETURNCMD is set you return the id, but
2462 do not send a message to the owner */
2463 if (0 == (Flags
& TPM_RETURNCMD
))
2465 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2467 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2468 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2472 if (MenuInfo
->dwStyle
& MNS_NOTIFYBYPOS
)
2473 PostMessageW(Mt
->OwnerWnd
, WM_MENUCOMMAND
,
2474 MenuInfo
->FocusedItem
,
2475 (LPARAM
)MenuInfo
->Self
);
2477 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2481 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2487 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2494 /***********************************************************************
2497 * Return TRUE if we can go on with menu tracking.
2499 static BOOL FASTCALL
2500 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2503 ROSMENUINFO MenuInfo
;
2504 ROSMENUITEMINFO Item
;
2506 TRACE("%x PtMenu=%p\n", Mt
, PtMenu
);
2510 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2514 if (IS_SYSTEM_MENU(&MenuInfo
))
2520 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2522 MenuInitRosMenuItemInfo(&Item
);
2523 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2525 MenuCleanupRosMenuItemInfo(&Item
);
2529 if (!(Item
.fType
& MF_SEPARATOR
) &&
2530 !(Item
.fState
& (MFS_DISABLED
| MFS_GRAYED
)) )
2532 if (MenuInfo
.FocusedItem
!= Index
)
2534 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2537 /* If the popup menu is not already "popped" */
2538 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2540 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2544 MenuCleanupRosMenuItemInfo(&Item
);
2549 /* else the click was on the menu bar, finish the tracking */
2554 /***********************************************************************
2557 * Return the value of MenuExecFocusedItem if
2558 * the selected item was not a popup. Else open the popup.
2559 * A -1 return value indicates that we go on with menu tracking.
2563 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2566 ROSMENUINFO MenuInfo
;
2567 ROSMENUITEMINFO ItemInfo
;
2569 TRACE("%p hmenu=%x\n", Mt
, PtMenu
);
2574 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2579 if (! IS_SYSTEM_MENU(&MenuInfo
))
2581 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2583 MenuInitRosMenuItemInfo(&ItemInfo
);
2584 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2585 MenuInfo
.FocusedItem
== Id
)
2587 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2589 INT ExecutedMenuId
= MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2590 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2591 return (ExecutedMenuId
< 0) ? -1 : ExecutedMenuId
;
2593 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2595 /* If we are dealing with the top-level menu */
2596 /* and this is a click on an already "popped" item: */
2597 /* Stop the menu tracking and close the opened submenus */
2598 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2600 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2604 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2605 MenuInfo
.TimeToHide
= TRUE
;
2606 MenuSetRosMenuInfo(&MenuInfo
);
2612 /***********************************************************************
2615 * Walks menu chain trying to find a menu pt maps to.
2617 static HMENU FASTCALL
2618 MenuPtMenu(HMENU Menu
, POINT Pt
)
2620 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2621 ROSMENUINFO MenuInfo
;
2622 ROSMENUITEMINFO ItemInfo
;
2626 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2631 /* try subpopup first (if any) */
2632 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2634 MenuInitRosMenuItemInfo(&ItemInfo
);
2635 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2636 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2637 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2639 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2642 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2646 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2649 /* check the current window (avoiding WM_HITTEST) */
2650 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2651 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2653 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2658 else if (HTSYSMENU
== Ht
)
2660 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2662 else if (HTMENU
== Ht
)
2664 Ret
= GetMenu(MenuInfo
.Wnd
);
2670 /***********************************************************************
2673 * Return TRUE if we can go on with menu tracking.
2675 static BOOL FASTCALL
2676 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2679 ROSMENUINFO MenuInfo
;
2680 ROSMENUITEMINFO ItemInfo
;
2684 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2688 if (IS_SYSTEM_MENU(&MenuInfo
))
2694 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2699 Index
= NO_SELECTED_ITEM
;
2702 if (NO_SELECTED_ITEM
== Index
)
2704 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2705 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2707 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2711 else if (MenuInfo
.FocusedItem
!= Index
)
2713 MenuInitRosMenuItemInfo(&ItemInfo
);
2714 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, Index
, &ItemInfo
) &&
2715 !(ItemInfo
.fType
& MF_SEPARATOR
))
2717 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2718 if (!(ItemInfo
.fState
& (MFS_DISABLED
| MFS_GRAYED
)))
2719 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2721 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2727 /***********************************************************************
2730 * Return the handle of the selected sub-popup menu (if any).
2732 static HMENU FASTCALL
2733 MenuGetSubPopup(HMENU Menu
)
2735 ROSMENUINFO MenuInfo
;
2736 ROSMENUITEMINFO ItemInfo
;
2738 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2739 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2744 MenuInitRosMenuItemInfo(&ItemInfo
);
2745 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2747 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2750 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2752 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2753 return ItemInfo
.hSubMenu
;
2756 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2760 /***********************************************************************
2763 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2765 static LRESULT FASTCALL
2766 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2768 ROSMENUINFO TopMenuInfo
;
2769 ROSMENUINFO MenuInfo
;
2771 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2773 return (LRESULT
) FALSE
;
2776 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2777 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2779 MDINEXTMENU NextMenu
;
2784 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2785 NextMenu
.hmenuNext
= NULL
;
2786 NextMenu
.hwndNext
= NULL
;
2787 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2789 TRACE("%p [%p] -> %p [%p]\n",
2790 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2792 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2794 DWORD Style
= GetWindowLongPtrW(Mt
->OwnerWnd
, GWL_STYLE
);
2795 NewWnd
= Mt
->OwnerWnd
;
2796 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2798 /* switch to the menu bar */
2800 if (0 != (Style
& WS_CHILD
)
2801 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2808 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2812 Id
= MenuInfo
.MenuItemCount
- 1;
2815 else if (0 != (Style
& WS_SYSMENU
))
2817 /* switch to the system menu */
2818 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2825 else /* application returned a new menu to switch to */
2827 NewMenu
= NextMenu
.hmenuNext
;
2828 NewWnd
= NextMenu
.hwndNext
;
2830 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2832 DWORD Style
= GetWindowLongPtrW(NewWnd
, GWL_STYLE
);
2834 if (0 != (Style
& WS_SYSMENU
)
2835 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2837 /* get the real system menu */
2838 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2840 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2842 /* FIXME: Not sure what to do here;
2843 * perhaps try to track NewMenu as a popup? */
2845 WARN(" -- got confused.\n");
2855 if (NewMenu
!= Mt
->TopMenu
)
2857 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2859 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2861 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2865 if (NewWnd
!= Mt
->OwnerWnd
)
2867 Mt
->OwnerWnd
= NewWnd
;
2868 SetCapture(Mt
->OwnerWnd
);
2869 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2872 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2873 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2875 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2884 /***********************************************************************
2887 * The idea is not to show the popup if the next input message is
2888 * going to hide it anyway.
2890 static BOOL FASTCALL
2891 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2895 Msg
.hwnd
= Mt
->OwnerWnd
;
2897 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2898 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2903 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2904 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2906 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2907 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2908 if (WM_KEYDOWN
== Msg
.message
2909 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2911 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2918 /* failures go through this */
2919 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2924 /***********************************************************************
2927 * Handle a VK_ESCAPE key event in a menu.
2929 static BOOL FASTCALL
2930 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2932 BOOL EndMenu
= TRUE
;
2933 ROSMENUINFO MenuInfo
;
2934 HMENU MenuTmp
, MenuPrev
;
2936 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2938 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2939 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2941 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2943 /* close topmost popup */
2944 while (MenuTmp
!= Mt
->CurrentMenu
)
2947 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2950 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2952 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2954 Mt
->CurrentMenu
= MenuPrev
;
2962 /***********************************************************************
2965 * Handle a VK_LEFT key event in a menu.
2967 static void FASTCALL
2968 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2970 ROSMENUINFO MenuInfo
;
2971 ROSMENUINFO TopMenuInfo
;
2972 ROSMENUINFO PrevMenuInfo
;
2973 HMENU MenuTmp
, MenuPrev
;
2976 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2978 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2983 /* Try to move 1 column left (if possible) */
2984 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2986 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2988 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2993 /* close topmost popup */
2994 while (MenuTmp
!= Mt
->CurrentMenu
)
2997 MenuTmp
= MenuGetSubPopup(MenuPrev
);
3000 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
3004 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
3005 Mt
->CurrentMenu
= MenuPrev
;
3007 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
3011 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
3013 /* move menu bar selection if no more popups are left */
3015 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
3017 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
3020 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
3022 /* A sublevel menu was displayed - display the next one
3023 * unless there is another displacement coming up */
3025 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
3026 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
3028 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
3035 /***********************************************************************
3038 * Handle a VK_RIGHT key event in a menu.
3040 static void FASTCALL
MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
3043 ROSMENUINFO MenuInfo
;
3044 ROSMENUINFO CurrentMenuInfo
;
3047 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3048 Mt
->CurrentMenu
, Mt
->TopMenu
);
3050 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
)) return;
3051 if ((MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
3053 /* If already displaying a popup, try to display sub-popup */
3055 hmenutmp
= Mt
->CurrentMenu
;
3056 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
3058 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
3061 /* if subpopup was displayed then we are done */
3062 if (hmenutmp
!= Mt
->CurrentMenu
) return;
3065 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
3070 /* Check to see if there's another column */
3071 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
3073 TRACE("Going to %d.\n", NextCol
);
3074 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
3076 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
3081 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
3083 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
3085 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
3086 hmenutmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
3093 /* try to move to the next item */
3094 if ( !MenuDoNextMenu(Mt
, VK_RIGHT
))
3095 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3097 if ( hmenutmp
|| Mt
->TrackFlags
& TF_SUSPENDPOPUP
)
3099 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
3100 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
3102 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
3109 /***********************************************************************
3112 * Menu tracking code.
3114 static INT FASTCALL
MenuTrackMenu(HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
3115 HWND hwnd
, const RECT
*lprect
)
3118 ROSMENUINFO MenuInfo
;
3119 ROSMENUITEMINFO ItemInfo
;
3121 INT executedMenuId
= -1;
3123 BOOL enterIdleSent
= FALSE
;
3126 mt
.CurrentMenu
= hmenu
;
3132 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3133 hmenu
, wFlags
, x
, y
, hwnd
, lprect
? lprect
->left
: 0, lprect
? lprect
->top
: 0,
3134 lprect
? lprect
->right
: 0, lprect
? lprect
->bottom
: 0);
3138 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3143 if (! MenuGetRosMenuInfo(&MenuInfo
, hmenu
))
3148 if (wFlags
& TPM_BUTTONDOWN
)
3150 /* Get the result in order to start the tracking or not */
3151 fRemove
= MenuButtonDown( &mt
, hmenu
, wFlags
);
3152 fEndMenu
= !fRemove
;
3155 SetCapture(mt
.OwnerWnd
);
3156 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, mt
.OwnerWnd
);
3158 ERR("MenuTrackMenu 1\n");
3161 PVOID menu
= ValidateHandle(mt
.CurrentMenu
, VALIDATE_TYPE_MENU
);
3162 if (!menu
) /* sometimes happens if I do a window manager close */
3165 /* we have to keep the message in the queue until it's
3166 * clear that menu loop is not over yet. */
3170 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3172 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3173 /* remove the message from the queue */
3174 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3180 HWND win
= (wFlags
& TPM_ENTERIDLEEX
) && (MenuInfo
.Flags
& MF_POPUP
) ? MenuInfo
.Wnd
: NULL
;
3181 enterIdleSent
= TRUE
;
3182 SendMessageW( mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) win
);
3188 /* check if EndMenu() tried to cancel us, by posting this message */
3189 if (msg
.message
== WM_CANCELMODE
)
3191 /* we are now out of the loop */
3194 /* remove the message from the queue */
3195 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3197 /* break out of internal loop, ala ESCAPE */
3201 TranslateMessage( &msg
);
3204 if ( (msg
.hwnd
== MenuInfo
.Wnd
) || (msg
.message
!=WM_TIMER
) )
3205 enterIdleSent
=FALSE
;
3208 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3211 * Use the mouse coordinates in lParam instead of those in the MSG
3212 * struct to properly handle synthetic messages. They are already
3213 * in screen coordinates.
3215 mt
.Pt
.x
= (short)LOWORD(msg
.lParam
);
3216 mt
.Pt
.y
= (short)HIWORD(msg
.lParam
);
3218 /* Find a menu for this mouse event */
3219 hmenu
= MenuPtMenu(mt
.TopMenu
, mt
.Pt
);
3223 /* no WM_NC... messages in captured state */
3225 case WM_RBUTTONDBLCLK
:
3226 case WM_RBUTTONDOWN
:
3227 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3229 case WM_LBUTTONDBLCLK
:
3230 case WM_LBUTTONDOWN
:
3231 /* If the message belongs to the menu, removes it from the queue */
3232 /* Else, end menu tracking */
3233 fRemove
= MenuButtonDown(&mt
, hmenu
, wFlags
);
3234 fEndMenu
= !fRemove
;
3238 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3241 /* Check if a menu was selected by the mouse */
3244 executedMenuId
= MenuButtonUp( &mt
, hmenu
, wFlags
);
3246 /* End the loop if executedMenuId is an item ID */
3247 /* or if the job was done (executedMenuId = 0). */
3248 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3250 /* No menu was selected by the mouse */
3251 /* if the function was called by TrackPopupMenu, continue
3252 with the menu tracking. If not, stop it */
3254 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3259 /* the selected menu item must be changed every time */
3260 /* the mouse moves. */
3263 fEndMenu
|= !MenuMouseMove( &mt
, hmenu
, wFlags
);
3266 } /* switch(msg.message) - mouse */
3268 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3270 fRemove
= TRUE
; /* Keyboard messages are always removed */
3284 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.CurrentMenu
))
3286 MenuSelectItem(mt
.OwnerWnd
, &MenuInfo
,
3287 NO_SELECTED_ITEM
, FALSE
, 0 );
3288 MenuMoveSelection(mt
.OwnerWnd
, &MenuInfo
,
3289 VK_HOME
== msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3294 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3295 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.CurrentMenu
))
3297 if (!(MenuInfo
.Flags
& MF_POPUP
))
3299 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.TopMenu
))
3300 mt
.CurrentMenu
= MenuShowSubPopup(mt
.OwnerWnd
, &MenuInfo
, TRUE
, wFlags
);
3302 else /* otherwise try to move selection */
3303 MenuMoveSelection(mt
.OwnerWnd
, &MenuInfo
,
3304 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3309 MenuKeyLeft( &mt
, wFlags
);
3313 MenuKeyRight( &mt
, wFlags
);
3317 fEndMenu
= MenuKeyEscape(&mt
, wFlags
);
3323 hi
.cbSize
= sizeof(HELPINFO
);
3324 hi
.iContextType
= HELPINFO_MENUITEM
;
3325 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.CurrentMenu
))
3327 if (MenuInfo
.FocusedItem
== NO_SELECTED_ITEM
)
3331 MenuInitRosMenuItemInfo(&ItemInfo
);
3332 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3333 MenuInfo
.FocusedItem
,
3336 hi
.iCtrlId
= ItemInfo
.wID
;
3342 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3345 hi
.hItemHandle
= hmenu
;
3346 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3347 hi
.MousePos
= msg
.pt
;
3348 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3355 break; /* WM_KEYDOWN */
3362 if (! MenuGetRosMenuInfo(&MenuInfo
, mt
.CurrentMenu
)) break;
3363 if (msg
.wParam
== L
'\r' || msg
.wParam
== L
' ')
3365 executedMenuId
= MenuExecFocusedItem(&mt
, &MenuInfo
, wFlags
);
3366 fEndMenu
= (executedMenuId
!= -2);
3370 /* Hack to avoid control chars. */
3371 /* We will find a better way real soon... */
3372 if (msg
.wParam
< 32) break;
3374 pos
= MenuFindItemByKey(mt
.OwnerWnd
, &MenuInfo
,
3375 LOWORD(msg
.wParam
), FALSE
);
3376 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3377 else if (pos
== (UINT
)-1) MessageBeep(0);
3380 MenuSelectItem(mt
.OwnerWnd
, &MenuInfo
, pos
,
3382 executedMenuId
= MenuExecFocusedItem(&mt
, &MenuInfo
, wFlags
);
3383 fEndMenu
= (executedMenuId
!= -2);
3387 } /* switch(msg.message) - kbd */
3391 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3392 DispatchMessageW( &msg
);
3396 if (!fEndMenu
) fRemove
= TRUE
;
3398 /* finally remove message from the queue */
3400 if (fRemove
&& !(mt
.TrackFlags
& TF_SKIPREMOVE
) )
3401 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3402 else mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3405 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3406 SetCapture(NULL
); /* release the capture */
3408 /* If dropdown is still painted and the close box is clicked on
3409 then the menu will be destroyed as part of the DispatchMessage above.
3410 This will then invalidate the menu handle in mt.hTopMenu. We should
3411 check for this first. */
3412 if( IsMenu( mt
.TopMenu
) )
3414 if (IsWindow(mt
.OwnerWnd
))
3416 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.TopMenu
))
3418 MenuHideSubPopups(mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3420 if (MenuInfo
.Flags
& MF_POPUP
)
3422 DestroyWindow(MenuInfo
.Wnd
);
3423 MenuInfo
.Wnd
= NULL
;
3425 if (!(MenuInfo
.Flags
& TPM_NONOTIFY
))
3426 SendMessageW( mt
.OwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.TopMenu
,
3427 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo
)) );
3430 MenuSelectItem( mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, 0 );
3433 SendMessageW( mt
.OwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0, 0xffff), 0 );
3436 /* Reset the variable for hiding menu */
3437 if (MenuGetRosMenuInfo(&MenuInfo
, mt
.TopMenu
))
3439 MenuInfo
.TimeToHide
= FALSE
;
3440 MenuSetRosMenuInfo(&MenuInfo
);
3444 /* The return value is only used by TrackPopupMenu */
3445 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3446 if (executedMenuId
== -1) executedMenuId
= 0;
3447 return executedMenuId
;
3450 /***********************************************************************
3453 static BOOL FASTCALL
MenuInitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3455 ROSMENUINFO MenuInfo
;
3457 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3461 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3462 if (!(wFlags
& TPM_NONOTIFY
))
3463 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3465 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3467 if (!(wFlags
& TPM_NONOTIFY
))
3469 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3471 MenuGetRosMenuInfo(&MenuInfo
, hMenu
);
3473 if (!MenuInfo
.Height
)
3475 /* app changed/recreated menu bar entries in WM_INITMENU
3476 Recalculate menu sizes else clicks will not work */
3477 SetWindowPos(hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3478 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3481 /* This makes the menus of applications built with Delphi work.
3482 * It also enables menus to be displayed in more than one window,
3483 * but there are some bugs left that need to be fixed in this case.
3485 if(MenuInfo
.Self
== hMenu
)
3487 MenuInfo
.Wnd
= hWnd
;
3488 MenuSetRosMenuInfo(&MenuInfo
);
3494 /***********************************************************************
3497 static BOOL FASTCALL
MenuExitTracking(HWND hWnd
)
3499 TRACE("hwnd=%p\n", hWnd
);
3501 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3507 /***********************************************************************
3508 * MenuTrackMouseMenuBar
3510 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3512 VOID
MenuTrackMouseMenuBar( HWND hWnd
, ULONG ht
, POINT pt
)
3514 HMENU hMenu
= (ht
== HTSYSMENU
) ? NtUserGetSystemMenu( hWnd
, FALSE
) : GetMenu(hWnd
);
3515 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3517 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd
, ht
, pt
.x
, pt
.y
);
3521 /* map point to parent client coordinates */
3522 HWND Parent
= GetAncestor(hWnd
, GA_PARENT
);
3523 if (Parent
!= GetDesktopWindow())
3525 ScreenToClient(Parent
, &pt
);
3528 MenuInitTracking(hWnd
, hMenu
, FALSE
, wFlags
);
3529 MenuTrackMenu(hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3530 MenuExitTracking(hWnd
);
3535 /***********************************************************************
3536 * MenuTrackKbdMenuBar
3538 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3540 VOID
MenuTrackKbdMenuBar(HWND hwnd
, UINT wParam
, WCHAR wChar
)
3542 UINT uItem
= NO_SELECTED_ITEM
;
3544 ROSMENUINFO MenuInfo
;
3545 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3547 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3549 /* find window that has a menu */
3551 while (!((GetWindowLongPtrW( hwnd
, GWL_STYLE
) &
3552 (WS_CHILD
| WS_POPUP
)) != WS_CHILD
))
3553 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3555 /* check if we have to track a system menu */
3557 hTrackMenu
= GetMenu( hwnd
);
3558 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3560 if (!(GetWindowLongPtrW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3561 hTrackMenu
= NtUserGetSystemMenu(hwnd
, FALSE
);
3563 wParam
|= HTSYSMENU
; /* prevent item lookup */
3566 if (!IsMenu( hTrackMenu
)) return;
3568 MenuInitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3570 if (! MenuGetRosMenuInfo(&MenuInfo
, hTrackMenu
))
3575 if( wChar
&& wChar
!= ' ' )
3577 uItem
= MenuFindItemByKey( hwnd
, &MenuInfo
, wChar
, (wParam
& HTSYSMENU
) );
3578 if ( uItem
>= (UINT
)(-2) )
3580 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3581 /* schedule end of menu tracking */
3582 wFlags
|= TF_ENDMENU
;
3587 MenuSelectItem( hwnd
, &MenuInfo
, uItem
, TRUE
, 0 );
3589 if (wParam
& HTSYSMENU
)
3591 /* prevent sysmenu activation for managed windows on Alt down/up */
3592 // if (GetPropA( hwnd, "__wine_x11_managed" ))
3593 wFlags
|= TF_ENDMENU
; /* schedule end of menu tracking */
3597 if( uItem
== NO_SELECTED_ITEM
)
3598 MenuMoveSelection( hwnd
, &MenuInfo
, ITEM_NEXT
);
3600 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3604 MenuTrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3605 MenuExitTracking( hwnd
);
3609 /**********************************************************************
3610 * TrackPopupMenuEx (USER32.@)
3612 BOOL WINAPI
TrackPopupMenuEx( HMENU Menu
, UINT Flags
, int x
, int y
,
3613 HWND Wnd
, LPTPMPARAMS Tpm
)
3615 /* Not fully implemented */
3616 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
3617 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
3620 /**********************************************************************
3621 * TrackPopupMenu (USER32.@)
3623 BOOL WINAPI
TrackPopupMenu( HMENU Menu
, UINT Flags
, int x
, int y
,
3624 int Reserved
, HWND Wnd
, CONST RECT
*Rect
)
3630 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3634 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
3636 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3637 if (!(Flags
& TPM_NONOTIFY
))
3638 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
3640 if (MenuShowPopup(Wnd
, Menu
, 0, Flags
, x
, y
, 0, 0 ))
3641 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
3642 MenuExitTracking(Wnd
);
3648 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3649 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3651 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3653 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3654 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3655 * MFT_STRING is replaced by MIIM_STRING.
3656 * (So, I guess we should use MIIM_STRING only for strings?)
3658 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3660 * Based on wine, SetMenuItemInfo_common:
3661 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3662 * it will result in a error.
3663 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3664 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3671 LPMENUITEMINFOW mii
,
3678 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3680 if(Flags
& MF_BITMAP
)
3682 mii
->fMask
|= MIIM_BITMAP
; /* Use the new way of seting hbmpItem.*/
3683 mii
->hbmpItem
= (HBITMAP
) NewItem
;
3685 if (Flags
& MF_HELP
)
3687 /* increase ident */
3688 mii
->fType
|= MF_HELP
;
3691 else if(Flags
& MF_OWNERDRAW
)
3693 mii
->fType
|= MFT_OWNERDRAW
;
3694 mii
->fMask
|= MIIM_DATA
;
3695 mii
->dwItemData
= (DWORD
) NewItem
;
3697 else if (Flags
& MF_SEPARATOR
)
3699 mii
->fType
|= MFT_SEPARATOR
;
3700 if (!(Flags
& (MF_GRAYED
|MF_DISABLED
)))
3701 Flags
|= MF_GRAYED
|MF_DISABLED
;
3703 else /* Default action MF_STRING. */
3705 /* Item beginning with a backspace is a help item */
3706 if (NewItem
!= NULL
)
3710 if (*NewItem
== '\b')
3712 mii
->fType
|= MF_HELP
;
3718 LPCSTR NewItemA
= (LPCSTR
) NewItem
;
3719 if (*NewItemA
== '\b')
3721 mii
->fType
|= MF_HELP
;
3723 NewItem
= (LPCWSTR
) NewItemA
;
3727 if (Flags
& MF_HELP
)
3728 mii
->fType
|= MF_HELP
;
3729 mii
->fMask
|= MIIM_STRING
;
3730 mii
->fType
|= MFT_STRING
; /* Zero */
3731 mii
->dwTypeData
= (LPWSTR
)NewItem
;
3733 mii
->cch
= (NULL
== NewItem
? 0 : strlenW(NewItem
));
3735 mii
->cch
= (NULL
== NewItem
? 0 : strlen((LPCSTR
)NewItem
));
3739 mii
->fType
|= MFT_SEPARATOR
;
3740 if (!(Flags
& (MF_GRAYED
|MF_DISABLED
)))
3741 Flags
|= MF_GRAYED
|MF_DISABLED
;
3745 if(Flags
& MF_RIGHTJUSTIFY
) /* Same as MF_HELP */
3747 mii
->fType
|= MFT_RIGHTJUSTIFY
;
3750 if(Flags
& MF_MENUBREAK
)
3752 mii
->fType
|= MFT_MENUBREAK
;
3754 else if(Flags
& MF_MENUBARBREAK
)
3756 mii
->fType
|= MFT_MENUBARBREAK
;
3759 if(Flags
& MF_GRAYED
|| Flags
& MF_DISABLED
)
3761 if (Flags
& MF_GRAYED
)
3762 mii
->fState
|= MF_GRAYED
;
3764 if (Flags
& MF_DISABLED
)
3765 mii
->fState
|= MF_DISABLED
;
3767 mii
->fMask
|= MIIM_STATE
;
3769 else if (Flags
& MF_HILITE
)
3771 mii
->fState
|= MF_HILITE
;
3772 mii
->fMask
|= MIIM_STATE
;
3774 else /* default state */
3776 mii
->fState
|= MFS_ENABLED
;
3777 mii
->fMask
|= MIIM_STATE
;
3780 if(Flags
& MF_POPUP
)
3782 mii
->fType
|= MF_POPUP
;
3783 mii
->fMask
|= MIIM_SUBMENU
;
3784 mii
->hSubMenu
= (HMENU
)IDNewItem
;
3788 mii
->fMask
|= MIIM_ID
;
3789 mii
->wID
= (UINT
)IDNewItem
;
3795 User32CallLoadMenuFromKernel(PVOID Arguments
, ULONG ArgumentLength
)
3797 PLOADMENU_CALLBACK_ARGUMENTS Common
;
3800 Common
= (PLOADMENU_CALLBACK_ARGUMENTS
) Arguments
;
3802 Result
= (LRESULT
)LoadMenuW( Common
->hModule
,
3803 IS_INTRESOURCE(Common
->MenuName
[0]) ?
3804 MAKEINTRESOURCE(Common
->MenuName
[0]) :
3805 (LPCWSTR
)&Common
->MenuName
);
3807 return ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
);
3811 /* FUNCTIONS *****************************************************************/
3814 MenuIsStringItem(ULONG TypeData)
3816 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3824 AppendMenuA(HMENU hMenu
,
3826 UINT_PTR uIDNewItem
,
3829 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3838 AppendMenuW(HMENU hMenu
,
3840 UINT_PTR uIDNewItem
,
3843 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3852 CheckMenuItem(HMENU hmenu
,
3856 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3861 MenuCheckMenuRadioItem(HMENU hMenu
, UINT idFirst
, UINT idLast
, UINT idCheck
, UINT uFlags
, BOOL bCheck
, PUINT pChecked
, PUINT pUnchecked
, PUINT pMenuChanged
)
3864 PROSMENUITEMINFO Items
= NULL
;
3865 UINT cChecked
, cUnchecked
;
3869 if(idFirst
> idLast
)
3872 ItemCount
= GetMenuItemCount(hMenu
);
3874 //mi.cbSize = sizeof(ROSMENUINFO);
3875 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3878 if(MenuGetAllRosMenuItemInfo(hMenu
, &Items
) <= 0)
3880 ERR("MenuGetAllRosMenuItemInfo failed\n");
3884 cChecked
= cUnchecked
= 0;
3886 for (i
= 0 ; i
< ItemCount
; i
++)
3889 if (0 != (Items
[i
].fType
& MF_MENUBARBREAK
)) continue;
3890 if (0 != (Items
[i
].fType
& MF_SEPARATOR
)) continue;
3892 if ((Items
[i
].fType
& MF_POPUP
) && (uFlags
== MF_BYCOMMAND
))
3894 MenuCheckMenuRadioItem(Items
[i
].hSubMenu
, idFirst
, idLast
, idCheck
, uFlags
, bCheck
, pChecked
, pUnchecked
, pMenuChanged
);
3897 if (uFlags
& MF_BYPOSITION
)
3899 if (i
< idFirst
|| i
> idLast
)
3914 if (Items
[i
].wID
< idFirst
|| Items
[i
].wID
> idLast
)
3917 if (Items
[i
].wID
== idCheck
)
3931 Items
[i
].fMask
= MIIM_STATE
| MIIM_FTYPE
;
3934 Items
[i
].fType
|= MFT_RADIOCHECK
;
3935 Items
[i
].fState
|= MFS_CHECKED
;
3939 Items
[i
].fState
&= ~MFS_CHECKED
;
3942 if(!MenuSetRosMenuItemInfo(hMenu
, i
,&Items
[i
]))
3944 ERR("MenuSetRosMenuItemInfo failed\n");
3949 HeapFree(GetProcessHeap(), 0, Items
);
3951 *pChecked
+= cChecked
;
3952 *pUnchecked
+= cUnchecked
;
3954 if (cChecked
|| cUnchecked
)
3964 CheckMenuRadioItem(HMENU hmenu
,
3971 UINT cUnchecked
= 0;
3972 UINT cMenuChanged
= 0;
3974 if (!MenuCheckMenuRadioItem(hmenu
, idFirst
, idLast
, idCheck
, uFlags
, FALSE
, &cChecked
, &cUnchecked
, &cMenuChanged
))
3977 if (cMenuChanged
> 1)
3984 if (!MenuCheckMenuRadioItem(hmenu
, idFirst
, idLast
, idCheck
, uFlags
, TRUE
, &cChecked
, &cUnchecked
, &cMenuChanged
))
3987 return (cChecked
!= 0);
3998 return (HMENU
)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU
);
4006 CreatePopupMenu(VOID
)
4009 return (HMENU
)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP
);
4017 DrawMenuBar(HWND hWnd
)
4019 // return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
4020 ROSMENUINFO MenuInfo
;
4022 hMenu
= GetMenu(hWnd
);
4025 MenuGetRosMenuInfo(&MenuInfo
, hMenu
);
4026 MenuInfo
.Height
= 0; // make sure to recalc size
4027 MenuSetRosMenuInfo(&MenuInfo
);
4028 /* The wine method doesn't work and I suspect it's more effort
4029 then hackfix solution
4030 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4031 SWP_NOZORDER | SWP_FRAMECHANGED );
4034 DefWndNCPaint(hWnd
,(HRGN
)-1,-1);
4043 EnableMenuItem(HMENU hMenu
,
4047 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
4057 guii
.cbSize
= sizeof(GUITHREADINFO
);
4058 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
4060 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
4072 PWND Wnd
= ValidateHwnd(hWnd
);
4077 return (HMENU
)Wnd
->IDMenu
;
4085 GetMenuCheckMarkDimensions(VOID
)
4087 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
4088 GetSystemMetrics(SM_CYMENUCHECK
)));
4096 GetMenuDefaultItem(HMENU hMenu
,
4100 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
4108 GetMenuInfo(HMENU hmenu
,
4114 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
4117 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
4118 mi
.cbSize
= sizeof(MENUINFO
);
4119 mi
.fMask
= lpcmi
->fMask
;
4121 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
4123 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
4132 GetMenuItemCount(HMENU Menu
)
4134 ROSMENUINFO MenuInfo
;
4136 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
4144 GetMenuItemID(HMENU hMenu
,
4147 ROSMENUITEMINFO mii
;
4149 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4150 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
4152 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
4157 if (NULL
!= mii
.hSubMenu
)
4178 LPMENUITEMINFOA mii
)
4184 if (mii
->cbSize
!= sizeof(MENUITEMINFOA
) &&
4185 mii
->cbSize
!= sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
))
4187 SetLastError(ERROR_INVALID_PARAMETER
);
4191 if(!(mii
->fMask
& (MIIM_TYPE
| MIIM_STRING
)))
4193 /* No text requested, just pass on */
4194 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
4197 AnsiBuffer
= mii
->dwTypeData
;
4198 Count
= miiW
.cch
= mii
->cch
;
4199 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
4200 miiW
.dwTypeData
= 0;
4204 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
4205 miiW
.cch
* sizeof(WCHAR
));
4206 if (miiW
.dwTypeData
== NULL
) return FALSE
;
4207 miiW
.dwTypeData
[0] = 0;
4210 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)&miiW
, FALSE
))
4212 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4216 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
);
4218 if (!AnsiBuffer
|| !Count
)
4220 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4221 mii
->dwTypeData
= AnsiBuffer
;
4222 mii
->cch
= miiW
.cch
;
4226 if ((miiW
.fMask
& MIIM_STRING
) || (IS_STRING_ITEM(miiW
.fType
)))
4230 if (!WideCharToMultiByte(CP_ACP
, 0, miiW
.dwTypeData
, miiW
.cch
, AnsiBuffer
, mii
->cch
, NULL
, NULL
))
4234 if (Count
> miiW
.cch
)
4236 AnsiBuffer
[miiW
.cch
] = 0;
4238 mii
->cch
= mii
->cch
;
4246 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4247 mii
->dwTypeData
= AnsiBuffer
;
4261 LPMENUITEMINFOW mii
)
4267 if (mii
->cbSize
!= sizeof(MENUITEMINFOW
) &&
4268 mii
->cbSize
!= sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
))
4270 SetLastError(ERROR_INVALID_PARAMETER
);
4274 if(!(mii
->fMask
& (MIIM_TYPE
| MIIM_STRING
)))
4276 /* No text requested, just pass on */
4277 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
4280 String
= mii
->dwTypeData
;
4282 RtlCopyMemory(&miiW
, mii
, mii
->cbSize
);
4283 miiW
.dwTypeData
= 0;
4287 miiW
.dwTypeData
= RtlAllocateHeap(GetProcessHeap(), 0,
4288 miiW
.cch
* sizeof(WCHAR
));
4289 if (miiW
.dwTypeData
== NULL
) return FALSE
;
4290 miiW
.dwTypeData
[0] = 0;
4293 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) &miiW
, FALSE
))
4295 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4299 RtlCopyMemory(mii
, &miiW
, miiW
.cbSize
); // Okay to over write user data.
4301 if (!String
|| !Count
)
4303 if (miiW
.dwTypeData
) RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4304 mii
->dwTypeData
= String
; // may not be zero.
4305 mii
->cch
= miiW
.cch
;
4309 if ((miiW
.fMask
& MIIM_STRING
) || (IS_STRING_ITEM(miiW
.fType
)))
4311 lstrcpynW( String
, miiW
.dwTypeData
, Count
);
4314 RtlFreeHeap(GetProcessHeap(), 0, miiW
.dwTypeData
);
4315 mii
->dwTypeData
= String
;
4316 mii
->cch
= strlenW(String
);
4331 ROSMENUINFO MenuInfo
;
4332 ROSMENUITEMINFO mii
;
4333 memset( &mii
, 0, sizeof(mii
) );
4334 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4335 mii
.fMask
= MIIM_STATE
| MIIM_FTYPE
| MIIM_SUBMENU
;
4338 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
4343 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
4347 nSubItems
= MenuInfo
.MenuItemCount
;
4349 /* FIXME - ported from wine, does that work (0xff)? */
4350 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
4351 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
4353 return (UINT
)-1; /* Invalid submenu */
4356 /* FIXME - ported from wine, does that work? */
4357 return (mii
.fType
| mii
.fState
);
4377 memset( &mii
, 0, sizeof(mii
) );
4378 mii
.dwTypeData
= lpString
;
4379 mii
.fMask
= MIIM_STRING
| MIIM_FTYPE
;
4380 mii
.fType
= MFT_STRING
;
4381 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4382 mii
.cch
= nMaxCount
;
4384 if(!(GetMenuItemInfoA( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&mii
)))
4404 memset( &miiW
, 0, sizeof(miiW
) );
4405 miiW
.dwTypeData
= lpString
;
4406 miiW
.fMask
= MIIM_STRING
| MIIM_FTYPE
;
4407 miiW
.fType
= MFT_STRING
;
4408 miiW
.cbSize
= sizeof(MENUITEMINFOW
);
4409 miiW
.cch
= nMaxCount
;
4411 if(!(GetMenuItemInfoW( hMenu
, uIDItem
, (BOOL
)(MF_BYPOSITION
& uFlag
),&miiW
)))
4429 mi
.cbSize
= sizeof(MENUITEMINFOW
);
4430 mi
.fMask
= MIIM_SUBMENU
;
4432 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
4434 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
4451 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
4453 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
4466 UINT_PTR uIDNewItem
,
4470 memset( &mii
, 0, sizeof(mii
) );
4471 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4472 mii
.fMask
= MIIM_FTYPE
;
4474 MenuSetItemData((LPMENUITEMINFOW
) &mii
,
4477 (LPCWSTR
) lpNewItem
,
4480 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4494 LPCMENUITEMINFOA lpmii
)
4497 UNICODE_STRING MenuText
;
4499 BOOL CleanHeap
= FALSE
;
4502 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4503 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4505 RtlCopyMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4507 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4509 mi
.cbSize
= sizeof( MENUITEMINFOW
);
4512 /* copy the text string */
4513 if (((mi
.fMask
& MIIM_STRING
) ||
4514 ((mi
.fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
)))
4515 && mi
.dwTypeData
!= NULL
)
4517 Status
= RtlCreateUnicodeStringFromAsciiz(&MenuText
, (LPSTR
)mi
.dwTypeData
);
4518 if (!NT_SUCCESS (Status
))
4520 SetLastError (RtlNtStatusToDosError(Status
));
4523 mi
.dwTypeData
= MenuText
.Buffer
;
4524 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4527 res
= NtUserThunkedMenuItemInfo(hMenu
, uItem
, fByPosition
, TRUE
, &mi
, NULL
);
4529 if ( CleanHeap
) RtlFreeUnicodeString ( &MenuText
);
4544 LPCMENUITEMINFOW lpmii
)
4547 UNICODE_STRING MenuText
;
4550 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4551 if a bad user passes bad data, we crash his process instead of the
4554 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4555 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4557 RtlCopyMemory(&mi
, lpmii
, lpmii
->cbSize
);
4559 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4561 mi
.cbSize
= sizeof( MENUITEMINFOW
);
4564 /* copy the text string */
4565 if (((mi
.fMask
& MIIM_STRING
) ||
4566 ((mi
.fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
)))
4567 && mi
.dwTypeData
!= NULL
)
4569 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4570 mi
.dwTypeData
= MenuText
.Buffer
;
4571 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4573 res
= NtUserThunkedMenuItemInfo(hMenu
, uItem
, fByPosition
, TRUE
, &mi
, NULL
);
4588 UINT_PTR uIDNewItem
,
4592 memset( &mii
, 0, sizeof(mii
) );
4593 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4594 mii
.fMask
= MIIM_FTYPE
;
4596 MenuSetItemData( &mii
,
4602 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)((MF_BYPOSITION
& uFlags
) > 0), &mii
);
4614 if (ValidateHandle(Menu
, VALIDATE_TYPE_MENU
)) return TRUE
;
4623 LoadMenuA(HINSTANCE hInstance
,
4626 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4627 if (Resource
== NULL
)
4631 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4639 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4641 return(LoadMenuIndirectW(lpMenuTemplate
));
4649 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4652 WORD version
, offset
;
4653 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4655 version
= GET_WORD(p
);
4660 case 0: /* standard format is version of 0 */
4661 offset
= GET_WORD(p
);
4662 p
+= sizeof(WORD
) + offset
;
4663 if (!(hMenu
= CreateMenu())) return 0;
4664 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4670 case 1: /* extended format is version of 1 */
4671 offset
= GET_WORD(p
);
4672 p
+= sizeof(WORD
) + offset
;
4673 if (!(hMenu
= CreateMenu())) return 0;
4674 if (!MENUEX_ParseResource(p
, hMenu
))
4676 DestroyMenu( hMenu
);
4681 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4691 LoadMenuW(HINSTANCE hInstance
,
4694 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4695 if (Resource
== NULL
)
4699 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4713 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4726 UINT_PTR uIDNewItem
,
4730 ROSMENUITEMINFO rmii
;
4732 memset( &mii
, 0, sizeof(mii
) );
4733 mii
.cbSize
= sizeof(MENUITEMINFOA
);
4734 mii
.fMask
= MIIM_FTYPE
;
4736 if (!MenuGetRosMenuInfo( &mi
, hMnu
)) return FALSE
;
4740 if (!MenuSetRosMenuInfo( &mi
)) return FALSE
;
4742 MenuInitRosMenuItemInfo( &rmii
);
4744 if(!MenuGetRosMenuItemInfo( hMnu
, uPosition
, &rmii
)) return FALSE
;
4746 if ((rmii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (rmii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4747 NtUserDestroyMenu( rmii
.hSubMenu
); /* ModifyMenu() spec */
4749 MenuCleanupRosMenuItemInfo( &rmii
);
4751 MenuSetItemData((LPMENUITEMINFOW
) &mii
,
4754 (LPCWSTR
) lpNewItem
,
4757 return SetMenuItemInfoA( hMnu
,
4759 (BOOL
)(MF_BYPOSITION
& uFlags
),
4773 UINT_PTR uIDNewItem
,
4777 ROSMENUITEMINFO rmii
;
4779 memset ( &mii
, 0, sizeof(mii
) );
4780 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4781 mii
.fMask
= MIIM_FTYPE
;
4783 if (!MenuGetRosMenuInfo( &mi
, hMnu
)) return FALSE
;
4785 mi
.Height
= 0; // Force size recalculation.
4787 if (!MenuSetRosMenuInfo( &mi
)) return FALSE
;
4789 MenuInitRosMenuItemInfo( &rmii
);
4791 if(!MenuGetRosMenuItemInfo( hMnu
, uPosition
, &rmii
)) return FALSE
;
4793 if ((rmii
.fType
& MF_POPUP
) && (uFlags
& MF_POPUP
) && (rmii
.hSubMenu
!= (HMENU
)uIDNewItem
))
4794 NtUserDestroyMenu( rmii
.hSubMenu
); /* ModifyMenu() spec */
4796 MenuCleanupRosMenuItemInfo( &rmii
);
4798 /* Init new data for this menu item */
4799 MenuSetItemData( &mii
,
4805 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4806 return SetMenuItemInfoW( hMnu
,
4808 (BOOL
)(MF_BYPOSITION
& uFlags
),
4820 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4836 if (!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
4838 SetLastError(ERROR_INVALID_PARAMETER
);
4842 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4843 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4856 HBITMAP hBitmapUnchecked
,
4857 HBITMAP hBitmapChecked
)
4859 ROSMENUITEMINFO uItem
;
4860 memset ( &uItem
, 0, sizeof(uItem
) );
4861 uItem
.fMask
= MIIM_STATE
| MIIM_BITMAP
;
4863 if(!(NtUserMenuItemInfo(hMenu
, uPosition
,
4864 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, FALSE
))) return FALSE
;
4866 if (!hBitmapChecked
&& !hBitmapUnchecked
)
4868 uItem
.fState
&= ~MF_USECHECKBITMAPS
;
4870 else /* Install new bitmaps */
4872 uItem
.hbmpChecked
= hBitmapChecked
;
4873 uItem
.hbmpUnchecked
= hBitmapUnchecked
;
4874 uItem
.fState
|= MF_USECHECKBITMAPS
;
4876 return NtUserMenuItemInfo(hMenu
, uPosition
,
4877 (BOOL
)(MF_BYPOSITION
& uFlags
), &uItem
, TRUE
);
4890 LPCMENUITEMINFOA lpmii
)
4892 MENUITEMINFOW MenuItemInfoW
;
4893 UNICODE_STRING UnicodeString
;
4895 ULONG Result
= FALSE
;
4897 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4899 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4901 MenuItemInfoW
.cbSize
= sizeof( MENUITEMINFOW
);
4902 MenuItemInfoW
.hbmpItem
= NULL
;
4905 * MIIM_STRING == good
4906 * MIIM_TYPE & MFT_STRING == good
4907 * MIIM_STRING & MFT_STRING == good
4908 * MIIM_STRING & MFT_OWNERSRAW == good
4910 if (((MenuItemInfoW
.fMask
& MIIM_STRING
) ||
4911 ((MenuItemInfoW
.fMask
& MIIM_TYPE
) &&
4912 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
)))
4913 && MenuItemInfoW
.dwTypeData
!= NULL
)
4915 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
4916 Status
= RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4917 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4918 if (!NT_SUCCESS (Status
))
4920 SetLastError (RtlNtStatusToDosError(Status
));
4923 MenuItemInfoW
.dwTypeData
= UnicodeString
.Buffer
;
4924 MenuItemInfoW
.cch
= UnicodeString
.Length
/ sizeof(WCHAR
);
4928 UnicodeString
.Buffer
= NULL
;
4931 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4932 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4934 if (UnicodeString
.Buffer
!= NULL
)
4936 RtlFreeUnicodeString(&UnicodeString
);
4952 LPCMENUITEMINFOW lpmii
)
4954 MENUITEMINFOW MenuItemInfoW
;
4957 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4959 if( lpmii
->cbSize
!= sizeof( MENUITEMINFOW
))
4961 MenuItemInfoW
.cbSize
= sizeof( MENUITEMINFOW
);
4962 MenuItemInfoW
.hbmpItem
= NULL
;
4965 if (((MenuItemInfoW
.fMask
& MIIM_STRING
) ||
4966 ((MenuItemInfoW
.fMask
& MIIM_TYPE
) &&
4967 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
)))
4968 && MenuItemInfoW
.dwTypeData
!= NULL
)
4970 MenuItemInfoW
.cch
= strlenW(MenuItemInfoW
.dwTypeData
);
4972 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4973 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4989 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4994 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4997 return NtUserSetSystemMenu(hwnd
, hMenu
);
5001 // Example for the Win32/User32 rewrite.
5002 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5016 return NtUserTrackPopupMenuEx( Menu
,
5021 NULL
); // LPTPMPARAMS is null
5030 GetMenuContextHelpId(HMENU hmenu
)
5033 mi
.cbSize
= sizeof(ROSMENUINFO
);
5034 mi
.fMask
= MIM_HELPID
;
5036 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
5038 return mi
.dwContextHelpID
;
5059 lResult
= PopupMenuWndProcA(hWnd
, Msg
, wParam
, lParam
);
5062 Result
= (ULONG_PTR
)lResult
;
5067 return NtUserMessageCall(hWnd
, Msg
, wParam
, lParam
, Result
, FNID_MENU
, TRUE
);
5087 lResult
= PopupMenuWndProcW(hWnd
, Msg
, wParam
, lParam
);
5090 Result
= (ULONG_PTR
)lResult
;
5095 return NtUserMessageCall(hWnd
, Msg
, wParam
, lParam
, Result
, FNID_MENU
, FALSE
);
5106 LPCWSTR lpszNewItem
,
5111 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5112 for MF_DELETE. We should check the parameters for all others
5113 MF_* actions also (anybody got a doc on ChangeMenu?).
5116 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
5119 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
5122 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
5125 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
5128 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
5129 flags
&~ MF_REMOVE
);
5131 default : /* MF_INSERT */
5132 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
5149 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5150 for MF_DELETE. We should check the parameters for all others
5151 MF_* actions also (anybody got a doc on ChangeMenu?).
5154 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
5157 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
5160 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
5163 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
5166 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
5167 flags
&~ MF_REMOVE
);
5169 default : /* MF_INSERT */
5170 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);