3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * Partly based on Wine code
6 * Copyright 1993 Martin Ayotte
7 * Copyright 1994 Alexandre Julliard
8 * Copyright 1997 Morten Welinder
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* $Id: menu.c,v 1.75 2004/12/11 20:18:06 navaraf Exp $
26 * PROJECT: ReactOS user32.dll
27 * FILE: lib/user32/windows/menu.c
29 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
31 * 09-05-2001 CSH Created
34 /* INCLUDES ******************************************************************/
42 #include <user32/callback.h>
43 #include "user32/regcontrol.h"
44 #include "../controls/controls.h"
49 /* internal popup menu window messages */
50 #define MM_SETMENUHANDLE (WM_USER + 0)
51 #define MM_GETMENUHANDLE (WM_USER + 1)
53 /* Internal MenuTrackMenu() flags */
54 #define TPM_INTERNAL 0xF0000000
55 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
56 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
57 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
59 /* TYPES *********************************************************************/
61 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
63 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
67 #define IS_SYSTEM_MENU(MenuInfo) \
68 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
70 #define IS_SYSTEM_POPUP(MenuInfo) \
71 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
73 #define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
75 #define MENU_BAR_ITEMS_SPACE (12)
76 #define SEPARATOR_HEIGHT (5)
77 #define MENU_TAB_SPACE (8)
83 #define MF_END (0x0080)
87 #define MIIM_STRING (0x00000040)
90 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
91 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
92 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
93 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
95 /* internal flags for menu tracking */
97 #define TF_ENDMENU 0x0001
98 #define TF_SUSPENDPOPUP 0x0002
99 #define TF_SKIPREMOVE 0x0004
104 HMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
105 HMENU TopMenu
; /* initial menu */
106 HWND OwnerWnd
; /* where notifications are sent */
110 static LRESULT WINAPI
PopupMenuWndProcW(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
112 /*********************************************************************
113 * PopupMenu class descriptor
115 const struct builtin_class_descr POPUPMENU_builtin_class
=
117 POPUPMENU_CLASS_ATOMW
, /* name */
118 CS_SAVEBITS
| CS_DBLCLKS
, /* style */
119 (WNDPROC
) PopupMenuWndProcW
, /* FIXME - procW */
120 (WNDPROC
) NULL
, /* FIXME - procA */
121 sizeof(MENUINFO
*), /* extra */
122 (LPCWSTR
) IDC_ARROW
, /* cursor */
123 (HBRUSH
)(COLOR_MENU
+ 1) /* brush */
127 /* INTERNAL FUNCTIONS ********************************************************/
129 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
130 * Of course I didnt copy the ASM code because we want this to be portable
131 * and it needs to go away.
135 #define GET_WORD(ptr) (*(WORD *)(ptr))
138 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
141 HFONT hMenuFont
= NULL
;
142 HFONT hMenuFontBold
= NULL
;
144 /* Flag set by EndMenu() to force an exit from menu tracking */
145 static BOOL fEndMenu
= FALSE
;
147 /* Use global popup window because there's no way 2 menus can
148 * be tracked at the same time. */
149 static HWND TopPopup
;
151 /* Dimension of the menu bitmaps */
152 static WORD ArrowBitmapWidth
= 0, ArrowBitmapHeight
= 0;
154 static HBITMAP StdMnArrow
= NULL
;
155 static HBITMAP BmpSysMenu
= NULL
;
157 /***********************************************************************
160 * Get full information about menu
163 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo
, HMENU Menu
)
165 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
166 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
168 return NtUserMenuInfo(Menu
, MenuInfo
, FALSE
);
171 /***********************************************************************
174 * Set full information about menu
177 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo
)
179 MenuInfo
->cbSize
= sizeof(ROSMENUINFO
);
180 MenuInfo
->fMask
= MIM_BACKGROUND
| MIM_HELPID
| MIM_MAXHEIGHT
| MIM_MENUDATA
| MIM_STYLE
;
182 return NtUserMenuInfo(MenuInfo
->Self
, MenuInfo
, TRUE
);
185 /***********************************************************************
186 * MenuInitRosMenuItemInfo
188 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
191 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
193 ZeroMemory(ItemInfo
, sizeof(ROSMENUITEMINFO
));
194 ItemInfo
->cbSize
= sizeof(ROSMENUITEMINFO
);
197 /***********************************************************************
198 * MenuGetRosMenuItemInfo
200 * Get full information about a menu item
202 #define INITIAL_STRING_SIZE 32 /* in WCHARs */
204 MenuGetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
206 UNICODE_STRING Text
= {0, 0, NULL
};
208 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
))
210 if (NULL
!= ItemInfo
->dwTypeData
)
212 /* There's already a buffer allocated */
213 Text
.Buffer
= ItemInfo
->dwTypeData
;
214 Text
.Length
= ItemInfo
->cch
* sizeof(WCHAR
);
215 Text
.MaximumLength
= (ItemInfo
->cch
< INITIAL_STRING_SIZE
? INITIAL_STRING_SIZE
216 : ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
220 Text
.Buffer
= HeapAlloc(GetProcessHeap(), 0, INITIAL_STRING_SIZE
* sizeof(WCHAR
));
221 if (NULL
== Text
.Buffer
)
226 Text
.MaximumLength
= INITIAL_STRING_SIZE
* sizeof(WCHAR
);
227 ItemInfo
->cch
= INITIAL_STRING_SIZE
- 1;
230 ItemInfo
->dwTypeData
= (LPWSTR
) &Text
;
232 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
233 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
235 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
237 if (NULL
!= Text
.Buffer
)
239 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
240 ItemInfo
->dwTypeData
= NULL
;
247 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
))
249 /* We have a string... */
250 if (Text
.MaximumLength
< (ItemInfo
->cch
+ 1) * sizeof(WCHAR
))
252 /* ...but we didn't allocate enough memory. Let's try again */
253 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
254 Text
.Buffer
= HeapAlloc(GetProcessHeap(), 0, (ItemInfo
->cch
+ 1) * sizeof(WCHAR
));
255 if (NULL
== Text
.Buffer
)
257 ItemInfo
->dwTypeData
= NULL
;
261 Text
.Length
= (ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
262 Text
.MaximumLength
= (ItemInfo
->cch
+ 1) * sizeof(WCHAR
);
264 if (! NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, FALSE
))
266 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
267 ItemInfo
->dwTypeData
= NULL
;
274 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
))
276 ItemInfo
->dwTypeData
= Text
.Buffer
;
277 ItemInfo
->cch
= Text
.Length
/ sizeof(WCHAR
);
278 Text
.Buffer
[ItemInfo
->cch
] = L
'\0';
282 /* Not a string, clean up the buffer */
283 if (NULL
!= Text
.Buffer
)
285 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
286 ItemInfo
->dwTypeData
= NULL
;
294 /***********************************************************************
295 * MenuSetRosMenuItemInfo
297 * Set full information about a menu item
300 MenuSetRosMenuItemInfo(HMENU Menu
, UINT Index
, PROSMENUITEMINFO ItemInfo
)
306 StringVal
= (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
);
309 Text
.Buffer
= ItemInfo
->dwTypeData
;
310 Text
.Length
= wcslen(Text
.Buffer
) * sizeof(WCHAR
);
311 Text
.MaximumLength
= Text
.Length
+ sizeof(WCHAR
);
312 ItemInfo
->dwTypeData
= (LPWSTR
) &Text
;
314 ItemInfo
->fMask
= MIIM_BITMAP
| MIIM_CHECKMARKS
| MIIM_DATA
| MIIM_FTYPE
315 | MIIM_ID
| MIIM_STATE
| MIIM_STRING
| MIIM_SUBMENU
| MIIM_TYPE
;
317 Ret
= NtUserMenuItemInfo(Menu
, Index
, TRUE
, ItemInfo
, TRUE
);
321 ItemInfo
->dwTypeData
= Text
.Buffer
;
327 /***********************************************************************
328 * MenuCleanupRosMenuItemInfo
330 * Cleanup after use of MenuGet/SetRosMenuItemInfo
333 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
335 if (MF_STRING
== MENU_ITEM_TYPE(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
337 HeapFree(GetProcessHeap(), 0, ItemInfo
->dwTypeData
);
338 ItemInfo
->dwTypeData
= NULL
;
343 /***********************************************************************
344 * MenuGetAllRosMenuItemInfo
346 * Get full information about all menu items
349 MenuGetAllRosMenuItemInfo(HMENU Menu
, PROSMENUITEMINFO
*ItemInfo
)
353 BufSize
= NtUserBuildMenuItemList(Menu
, (VOID
*) 1, 0, 0);
358 *ItemInfo
= HeapAlloc(GetProcessHeap(), 0, BufSize
);
359 if (NULL
== *ItemInfo
)
364 return NtUserBuildMenuItemList(Menu
, *ItemInfo
, BufSize
, 0);
367 /***********************************************************************
368 * MenuCleanupAllRosMenuItemInfo
370 * Cleanup after use of MenuGetAllRosMenuItemInfo
373 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo
)
375 HeapFree(GetProcessHeap(), 0, ItemInfo
);
379 /***********************************************************************
382 * Load the arrow bitmap. We can't do this from MenuInit since user32
383 * can also be used (and thus initialized) from text-mode.
386 MenuLoadBitmaps(VOID
)
388 /* Load menu bitmaps */
389 if (NULL
== StdMnArrow
)
391 StdMnArrow
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
393 if (NULL
!= StdMnArrow
)
396 GetObjectW(StdMnArrow
, sizeof(BITMAP
), &bm
);
397 ArrowBitmapWidth
= bm
.bmWidth
;
398 ArrowBitmapHeight
= bm
.bmHeight
;
402 /* Load system buttons bitmaps */
403 if (NULL
== BmpSysMenu
)
405 BmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
409 /***********************************************************************
410 * MenuGetBitmapItemSize
412 * Get the size of a bitmap item.
415 MenuGetBitmapItemSize(UINT Id
, DWORD Data
, SIZE
*Size
)
418 HBITMAP Bmp
= (HBITMAP
) Id
;
420 Size
->cx
= Size
->cy
= 0;
422 /* check if there is a magic menu item associated with this item */
423 if (0 != Id
&& IS_MAGIC_ITEM(Id
))
427 case (INT_PTR
) HBMMENU_SYSTEM
:
430 Bmp
= (HBITMAP
) Data
;
434 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
435 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
436 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
437 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
438 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
439 /* FIXME: Why we need to subtract these magic values? */
440 Size
->cx
= GetSystemMetrics(SM_CXSIZE
) - 2;
441 Size
->cy
= GetSystemMetrics(SM_CYSIZE
) - 4;
443 case (INT_PTR
) HBMMENU_CALLBACK
:
444 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
445 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
446 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
447 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
449 DPRINT("Magic menu bitmap not implemented\n");
454 if (GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
456 Size
->cx
= Bm
.bmWidth
;
457 Size
->cy
= Bm
.bmHeight
;
461 /***********************************************************************
464 * Draw a bitmap item.
467 MenuDrawBitmapItem(HDC Dc
, PROSMENUITEMINFO Item
, const RECT
*Rect
, BOOL MenuBar
)
472 HBITMAP Bmp
= (HBITMAP
) Item
->hbmpItem
;
473 int w
= Rect
->right
- Rect
->left
;
474 int h
= Rect
->bottom
- Rect
->top
;
478 /* Check if there is a magic menu item associated with this item */
479 if (IS_MAGIC_ITEM(Item
->hbmpItem
))
485 switch ((int) Item
->hbmpItem
)
487 case (INT_PTR
) HBMMENU_SYSTEM
:
488 if (NULL
!= Item
->hbmpItem
)
490 Bmp
= Item
->hbmpItem
;
491 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
499 if (! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
503 /* only use right half of the bitmap */
504 BmpXoffset
= Bm
.bmWidth
/ 2;
505 Bm
.bmWidth
-= BmpXoffset
;
508 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
509 Flags
= DFCS_CAPTIONRESTORE
;
511 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
513 Flags
= DFCS_CAPTIONMIN
;
515 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
517 Flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
519 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
520 Flags
= DFCS_CAPTIONCLOSE
;
522 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
523 Flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
525 case (INT_PTR
) HBMMENU_CALLBACK
:
526 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
527 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
528 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
529 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
531 DPRINT("Magic menu bitmap not implemented\n");
534 InflateRect(&r
, -1, -1);
535 if (0 != (Item
->fState
& MF_HILITE
))
537 Flags
|= DFCS_PUSHED
;
539 DrawFrameControl(Dc
, &r
, DFC_CAPTION
, Flags
);
543 if (NULL
== Bmp
|| ! GetObjectW(Bmp
, sizeof(BITMAP
), &Bm
))
549 DcMem
= CreateCompatibleDC(Dc
);
550 SelectObject(DcMem
, Bmp
);
552 /* handle fontsize > bitmap_height */
553 Top
= (Bm
.bmHeight
< h
) ? Rect
->top
+ (h
- Bm
.bmHeight
) / 2 : Rect
->top
;
555 Rop
= (0 != (Item
->fState
& MF_HILITE
) && ! IS_MAGIC_ITEM(Item
->hbmpItem
)) ? NOTSRCCOPY
: SRCCOPY
;
556 if (0 != (Item
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(Item
->fType
))
558 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
560 BitBlt(Dc
, Left
, Top
, w
, h
, DcMem
, BmpXoffset
, 0, Rop
);
564 /***********************************************************************
567 * Draw a single menu item.
570 MenuDrawMenuItem(HWND Wnd
, PROSMENUINFO MenuInfo
, HWND WndOwner
, HDC Dc
,
571 PROSMENUITEMINFO Item
, UINT Height
, BOOL MenuBar
, UINT Action
)
576 if (0 != (Item
->fType
& MF_SYSMENU
))
580 UserGetInsideRectNC(Wnd
, &Rect
);
581 UserDrawSysMenuButton(Wnd
, Dc
, &Rect
,
582 Item
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
590 if (0 != (Item
->fState
& MF_HILITE
))
594 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
595 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
599 if (0 != (Item
->fState
& MF_GRAYED
))
601 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
605 SetTextColor(Dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
607 SetBkColor(Dc
, GetSysColor(COLOR_HIGHLIGHT
));
612 if (0 != (Item
->fState
& MF_GRAYED
))
614 SetTextColor(Dc
, GetSysColor(COLOR_GRAYTEXT
));
618 SetTextColor(Dc
, GetSysColor(COLOR_MENUTEXT
));
620 SetBkColor(Dc
, GetSysColor(COLOR_MENU
));
623 if (0 != (Item
->fType
& MF_OWNERDRAW
))
626 ** Experimentation under Windows reveals that an owner-drawn
627 ** menu is given the rectangle which includes the space it requested
628 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
629 ** and a popup-menu arrow. This is the value of lpitem->rect.
630 ** Windows will leave all drawing to the application except for
631 ** the popup-menu arrow. Windows always draws that itself, after
632 ** the menu owner has finished drawing.
636 dis
.CtlType
= ODT_MENU
;
638 dis
.itemID
= Item
->wID
;
639 dis
.itemData
= (DWORD
)Item
->dwItemData
;
641 if (0 != (Item
->fState
& MF_CHECKED
))
643 dis
.itemState
|= ODS_CHECKED
;
645 if (0 != (Item
->fState
& MF_GRAYED
))
647 dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
649 if (0 != (Item
->fState
& MF_HILITE
))
651 dis
.itemState
|= ODS_SELECTED
;
653 dis
.itemAction
= Action
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
654 dis
.hwndItem
= (HWND
) MenuInfo
->Self
;
656 dis
.rcItem
= Item
->Rect
;
657 DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
658 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
659 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
660 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
662 SendMessageW(WndOwner
, WM_DRAWITEM
, 0, (LPARAM
) &dis
);
663 /* Fall through to draw popup-menu arrow */
666 DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item
->Rect
.left
, Item
->Rect
.top
,
667 Item
->Rect
.right
, Item
->Rect
.bottom
);
669 if (MenuBar
&& 0 != (Item
->fType
& MF_SEPARATOR
))
676 if (0 == (Item
->fType
& MF_OWNERDRAW
))
678 if (Item
->fState
& MF_HILITE
)
682 DrawEdge(Dc
, &Rect
, BDR_SUNKENOUTER
, BF_RECT
);
686 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
691 FillRect(Dc
, &Rect
, GetSysColorBrush(COLOR_MENU
));
695 SetBkMode(Dc
, TRANSPARENT
);
697 if (0 == (Item
->fType
& MF_OWNERDRAW
))
699 /* vertical separator */
700 if (! MenuBar
&& 0 != (Item
->fType
& MF_MENUBARBREAK
))
704 rc
.bottom
= Height
- 3;
705 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
708 /* horizontal separator */
709 if (0 != (Item
->fType
& MF_SEPARATOR
))
714 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
715 DrawEdge(Dc
, &rc
, EDGE_ETCHED
, BF_TOP
);
722 /* helper lines for debugging */
723 FrameRect(Dc
, &Rect
, GetStockObject(BLACK_BRUSH
));
724 SelectObject(Dc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
));
725 MoveToEx(Dc
, Rect
.left
, (Rect
.top
+ Rect
.bottom
) / 2, NULL
);
726 LineTo(Dc
, Rect
.right
, (Rect
.top
+ Rect
.bottom
) / 2);
731 INT y
= Rect
.top
+ Rect
.bottom
;
732 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
733 UINT CheckBitmapHeight
= GetSystemMetrics(SM_CYMENUCHECK
);
735 if (0 == (Item
->fType
& MF_OWNERDRAW
))
737 /* Draw the check mark
740 * Custom checkmark bitmaps are monochrome but not always 1bpp.
742 HBITMAP bm
= 0 != (Item
->fState
& MF_CHECKED
) ? Item
->hbmpChecked
: Item
->hbmpUnchecked
;
743 if (NULL
!= bm
) /* we have a custom bitmap */
745 HDC DcMem
= CreateCompatibleDC(Dc
);
746 SelectObject(DcMem
, bm
);
747 BitBlt(Dc
, Rect
.left
, (y
- CheckBitmapHeight
) / 2,
748 CheckBitmapWidth
, CheckBitmapHeight
,
749 DcMem
, 0, 0, SRCCOPY
);
752 else if (0 != (Item
->fState
& MF_CHECKED
)) /* standard bitmaps */
755 HBITMAP bm
= CreateBitmap(CheckBitmapWidth
, CheckBitmapHeight
, 1, 1, NULL
);
756 HDC DcMem
= CreateCompatibleDC(Dc
);
757 SelectObject(DcMem
, bm
);
758 SetRect( &r
, 0, 0, CheckBitmapWidth
, CheckBitmapHeight
);
759 DrawFrameControl(DcMem
, &r
, DFC_MENU
,
760 0 != (Item
->fType
& MFT_RADIOCHECK
) ?
761 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
762 BitBlt(Dc
, Rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
763 DcMem
, 0, 0, SRCCOPY
);
769 /* Draw the popup-menu arrow */
770 if (0 != (Item
->fType
& MF_POPUP
))
772 HDC DcMem
= CreateCompatibleDC(Dc
);
775 OrigBitmap
= SelectObject(DcMem
, StdMnArrow
);
776 BitBlt(Dc
, Rect
.right
- ArrowBitmapWidth
- 1,
777 (y
- ArrowBitmapHeight
) / 2,
778 ArrowBitmapWidth
, ArrowBitmapHeight
,
779 DcMem
, 0, 0, SRCCOPY
);
780 SelectObject(DcMem
, OrigBitmap
);
784 Rect
.left
+= CheckBitmapWidth
;
785 Rect
.right
-= ArrowBitmapWidth
;
788 /* Done for owner-drawn */
789 if (0 != (Item
->fType
& MF_OWNERDRAW
))
794 /* Draw the item text or bitmap */
795 if (IS_BITMAP_ITEM(Item
->fType
))
797 MenuDrawBitmapItem(Dc
, Item
, &Rect
, MenuBar
);
800 /* No bitmap - process text if present */
801 else if (IS_STRING_ITEM(Item
->fType
))
804 HFONT FontOld
= NULL
;
806 UINT uFormat
= MenuBar
? DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
807 : DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
809 if (0 != (Item
->fState
& MFS_DEFAULT
))
811 FontOld
= SelectObject(Dc
, hMenuFontBold
);
816 Rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
817 Rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
820 Text
= (PWCHAR
) Item
->dwTypeData
;
821 for (i
= 0; L
'\0' != Text
[i
]; i
++)
823 if (L
'\t' == Text
[i
] || L
'\b' == Text
[i
])
829 if (0 != (Item
->fState
& MF_GRAYED
))
831 if (0 == (Item
->fState
& MF_HILITE
))
833 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
834 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
835 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
836 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
838 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
841 DrawTextW(Dc
, Text
, i
, &Rect
, uFormat
);
843 /* paint the shortcut text */
844 if (! MenuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
846 if (L
'\t' == Text
[i
])
848 Rect
.left
= Item
->XTab
;
849 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
853 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
856 if (0 != (Item
->fState
& MF_GRAYED
))
858 if (0 == (Item
->fState
& MF_HILITE
))
860 ++Rect
.left
; ++Rect
.top
; ++Rect
.right
; ++Rect
.bottom
;
861 SetTextColor(Dc
, RGB(0xff, 0xff, 0xff));
862 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
863 --Rect
.left
; --Rect
.top
; --Rect
.right
; --Rect
.bottom
;
865 SetTextColor(Dc
, RGB(0x80, 0x80, 0x80));
867 DrawTextW(Dc
, Text
+ i
+ 1, -1, &Rect
, uFormat
);
872 SelectObject(Dc
, FontOld
);
877 /***********************************************************************
880 * Paint a popup menu.
883 MenuDrawPopupMenu(HWND Wnd
, HDC Dc
, HMENU Menu
)
885 HBRUSH PrevBrush
= NULL
;
888 ROSMENUINFO MenuInfo
;
889 ROSMENUITEMINFO ItemInfo
;
892 DPRINT("wnd=%x dc=%x menu=%x\n", Wnd
, Dc
, Menu
);
894 GetClientRect(Wnd
, &Rect
);
896 if (NULL
!= (PrevBrush
= SelectObject(Dc
, GetSysColorBrush(COLOR_MENU
)))
897 && NULL
!= SelectObject(Dc
, hMenuFont
))
899 Rectangle(Dc
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
901 PrevPen
= SelectObject(Dc
, GetStockObject(NULL_PEN
));
904 DrawEdge(Dc
, &Rect
, EDGE_RAISED
, BF_RECT
);
906 /* draw menu items */
908 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 != MenuInfo
.MenuItemCount
)
910 MenuInitRosMenuItemInfo(&ItemInfo
);
912 for (u
= 0; u
< MenuInfo
.MenuItemCount
; u
++)
914 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, u
, &ItemInfo
))
916 MenuDrawMenuItem(Wnd
, &MenuInfo
, MenuInfo
.WndOwner
, Dc
, &ItemInfo
,
917 MenuInfo
.Height
, FALSE
, ODA_DRAWENTIRE
);
921 MenuCleanupRosMenuItemInfo(&ItemInfo
);
926 SelectObject(Dc
, PrevBrush
);
931 static LRESULT WINAPI
932 PopupMenuWndProcW(HWND Wnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
934 DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd
, Message
, wParam
, lParam
);
940 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*) lParam
;
941 SetWindowLongW(Wnd
, 0, (LONG
) cs
->lpCreateParams
);
945 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
946 return MA_NOACTIVATE
;
951 BeginPaint(Wnd
, &ps
);
952 MenuDrawPopupMenu(Wnd
, ps
.hdc
, (HMENU
)GetWindowLongW(Wnd
, 0));
961 /* zero out global pointer in case resident popup window was destroyed. */
971 if (0 == GetWindowLongW(Wnd
, 0))
973 OutputDebugStringA("no menu to display\n");
978 SetWindowLongW(Wnd
, 0, 0);
982 case MM_SETMENUHANDLE
:
983 SetWindowLongW(Wnd
, 0, wParam
);
986 case MM_GETMENUHANDLE
:
987 return GetWindowLongW(Wnd
, 0);
990 return DefWindowProcW(Wnd
, Message
, wParam
, lParam
);
996 /**********************************************************************
997 * MENUEX_ParseResource
999 * Parse an extended menu resource and add items to the menu.
1000 * Return a pointer to the end of the resource.
1002 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
1004 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1012 mii
.cbSize
= sizeof(mii
);
1013 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
1014 mii
.fType
= GET_DWORD(res
);
1015 res
+= sizeof(DWORD
);
1016 mii
.fState
= GET_DWORD(res
);
1017 res
+= sizeof(DWORD
);
1018 mii
.wID
= GET_DWORD(res
);
1019 res
+= sizeof(DWORD
);
1020 resinfo
= GET_WORD(res
);
1021 res
+= sizeof(WORD
);
1022 /* Align the text on a word boundary. */
1023 res
+= (~((int)res
- 1)) & 1;
1024 mii
.dwTypeData
= (LPWSTR
) res
;
1025 res
+= (1 + wcslen(mii
.dwTypeData
)) * sizeof(WCHAR
);
1026 /* Align the following fields on a dword boundary. */
1027 res
+= (~((int)res
- 1)) & 3;
1029 if (resinfo
& 1) /* Pop-up? */
1031 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1032 res
+= sizeof(DWORD
);
1033 mii
.hSubMenu
= CreatePopupMenu();
1036 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
1038 DestroyMenu(mii
.hSubMenu
);
1041 mii
.fMask
|= MIIM_SUBMENU
;
1042 mii
.fType
|= MF_POPUP
;
1044 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1046 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1047 mii
.wID
, mii
.fType
);
1048 mii
.fType
|= MF_SEPARATOR
;
1050 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1052 while (!(resinfo
& MF_END
));
1057 /**********************************************************************
1058 * MENU_ParseResource
1060 * Parse a standard menu resource and add items to the menu.
1061 * Return a pointer to the end of the resource.
1063 * NOTE: flags is equivalent to the mtOption field
1065 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1074 flags
= GET_WORD(res
);
1076 /* remove MF_END flag before passing it to AppendMenu()! */
1077 end
= (flags
& MF_END
);
1078 if(end
) flags
^= MF_END
;
1080 res
+= sizeof(WORD
);
1081 if(!(flags
& MF_POPUP
))
1084 res
+= sizeof(WORD
);
1088 res
+= strlen(str
) + 1;
1090 res
+= (wcslen((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1091 if (flags
& MF_POPUP
)
1093 hSubMenu
= CreatePopupMenu();
1094 if(!hSubMenu
) return NULL
;
1095 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
1098 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
1100 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1102 else /* Not a popup */
1105 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
1107 AppendMenuW(hMenu
, flags
, id
,
1108 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1117 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
1121 hUser32
= GetModuleHandleW(L
"USER32");
1122 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
1123 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
1130 NONCLIENTMETRICSW ncm
;
1132 /* get the menu font */
1133 if(!hMenuFont
|| !hMenuFontBold
)
1135 ncm
.cbSize
= sizeof(ncm
);
1136 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
1138 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1142 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1143 if(hMenuFont
== NULL
)
1145 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1149 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
1150 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
1151 if(hMenuFontBold
== NULL
)
1153 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1162 /***********************************************************************
1165 * Calculate the size of the menu item and store it in ItemInfo->rect.
1167 static void FASTCALL
1168 MenuCalcItemSize(HDC Dc
, PROSMENUITEMINFO ItemInfo
, HWND WndOwner
,
1169 INT OrgX
, INT OrgY
, BOOL MenuBar
)
1172 UINT CheckBitmapWidth
= GetSystemMetrics(SM_CXMENUCHECK
);
1174 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc
, WndOwner
, OrgX
, OrgY
);
1176 SetRect(&ItemInfo
->Rect
, OrgX
, OrgY
, OrgX
, OrgY
);
1178 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1181 ** Experimentation under Windows reveals that an owner-drawn
1182 ** menu is expected to return the size of the content part of
1183 ** the menu item, not including the checkmark nor the submenu
1184 ** arrow. Windows adds those values itself and returns the
1185 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1187 MEASUREITEMSTRUCT mis
;
1188 mis
.CtlType
= ODT_MENU
;
1190 mis
.itemID
= ItemInfo
->wID
;
1191 mis
.itemData
= (DWORD
)ItemInfo
->dwItemData
;
1194 SendMessageW(WndOwner
, WM_MEASUREITEM
, 0, (LPARAM
) &mis
);
1195 ItemInfo
->Rect
.right
+= mis
.itemWidth
;
1199 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1201 /* under at least win95 you seem to be given a standard
1202 height for the menu and the height value is ignored */
1204 ItemInfo
->Rect
.bottom
+= GetSystemMetrics(SM_CYMENU
) - 1;
1208 ItemInfo
->Rect
.bottom
+= mis
.itemHeight
;
1211 DPRINT("id=%04x size=%dx%d\n", ItemInfo
->wID
, mis
.itemWidth
, mis
.itemHeight
);
1212 /* Fall through to get check/arrow width calculation. */
1215 if (0 != (ItemInfo
->fType
& MF_SEPARATOR
))
1217 ItemInfo
->Rect
.bottom
+= SEPARATOR_HEIGHT
;
1223 ItemInfo
->Rect
.right
+= 2 * CheckBitmapWidth
;
1224 if (0 != (ItemInfo
->fType
& MF_POPUP
))
1226 ItemInfo
->Rect
.right
+= ArrowBitmapWidth
;
1230 if (0 != (ItemInfo
->fType
& MF_OWNERDRAW
))
1235 if (IS_BITMAP_ITEM(ItemInfo
->fType
))
1239 MenuGetBitmapItemSize((int) ItemInfo
->hbmpItem
, (DWORD
) ItemInfo
->hbmpItem
, &Size
);
1240 ItemInfo
->Rect
.right
+= Size
.cx
;
1241 ItemInfo
->Rect
.bottom
+= Size
.cy
;
1243 /* Leave space for the sunken border */
1244 ItemInfo
->Rect
.right
+= 2;
1245 ItemInfo
->Rect
.bottom
+= 2;
1247 /* Special case: Minimize button doesn't have a space behind it. */
1248 if (ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1249 ItemInfo
->hbmpItem
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1250 ItemInfo
->Rect
.right
-= 1;
1253 /* it must be a text item - unless it's the system menu */
1254 if (0 == (ItemInfo
->fType
& MF_SYSMENU
) && IS_STRING_ITEM(ItemInfo
->fType
))
1258 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1259 wcslen((LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1261 ItemInfo
->Rect
.right
+= Size
.cx
;
1262 ItemInfo
->Rect
.bottom
+= max(Size
.cy
, GetSystemMetrics(SM_CYMENU
) - 1);
1267 ItemInfo
->Rect
.right
+= MENU_BAR_ITEMS_SPACE
;
1269 else if ((p
= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\t' )) != NULL
)
1271 /* Item contains a tab (only meaningful in popup menus) */
1272 GetTextExtentPoint32W(Dc
, (LPWSTR
) ItemInfo
->dwTypeData
,
1273 (int)(p
- (LPWSTR
) ItemInfo
->dwTypeData
), &Size
);
1274 ItemInfo
->XTab
= CheckBitmapWidth
+ MENU_TAB_SPACE
+ Size
.cx
;
1275 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1279 if (NULL
!= wcschr((LPWSTR
) ItemInfo
->dwTypeData
, L
'\b'))
1281 ItemInfo
->Rect
.right
+= MENU_TAB_SPACE
;
1283 ItemInfo
->XTab
= ItemInfo
->Rect
.right
- CheckBitmapWidth
1288 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo
->Rect
.left
, ItemInfo
->Rect
.top
, ItemInfo
->Rect
.right
, ItemInfo
->Rect
.bottom
);
1291 /***********************************************************************
1292 * MenuPopupMenuCalcSize
1294 * Calculate the size of a popup menu.
1296 static void FASTCALL
1297 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo
, HWND WndOwner
)
1299 ROSMENUITEMINFO ItemInfo
;
1302 int OrgX
, OrgY
, MaxX
, MaxTab
, MaxTabWidth
;
1304 MenuInfo
->Width
= MenuInfo
->Height
= 0;
1305 if (0 == MenuInfo
->MenuItemCount
)
1307 MenuSetRosMenuInfo(MenuInfo
);
1312 SelectObject(Dc
, hMenuFont
);
1317 MenuInitRosMenuItemInfo(&ItemInfo
);
1318 while (Start
< MenuInfo
->MenuItemCount
)
1323 MaxTab
= MaxTabWidth
= 0;
1325 /* Parse items until column break or end of menu */
1326 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1328 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1330 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1331 MenuSetRosMenuInfo(MenuInfo
);
1335 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1340 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, FALSE
);
1341 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1343 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1344 MenuSetRosMenuInfo(MenuInfo
);
1348 if (0 != (ItemInfo
.fType
& MF_MENUBARBREAK
))
1352 MaxX
= max(MaxX
, ItemInfo
.Rect
.right
);
1353 OrgY
= ItemInfo
.Rect
.bottom
;
1354 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1356 MaxTab
= max(MaxTab
, ItemInfo
.XTab
);
1357 MaxTabWidth
= max(MaxTabWidth
, ItemInfo
.Rect
.right
- ItemInfo
.XTab
);
1361 /* Finish the column (set all items to the largest width found) */
1362 MaxX
= max(MaxX
, MaxTab
+ MaxTabWidth
);
1365 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1367 ItemInfo
.Rect
.right
= MaxX
;
1368 if (IS_STRING_ITEM(ItemInfo
.fType
) && 0 != ItemInfo
.XTab
)
1370 ItemInfo
.XTab
= MaxTab
;
1372 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1376 MenuInfo
->Height
= max(MenuInfo
->Height
, OrgY
);
1379 MenuInfo
->Width
= MaxX
;
1381 /* space for 3d border */
1382 MenuInfo
->Height
+= 2;
1383 MenuInfo
->Width
+= 2;
1385 ReleaseDC(NULL
, Dc
);
1386 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1387 MenuSetRosMenuInfo(MenuInfo
);
1390 /***********************************************************************
1391 * MenuMenuBarCalcSize
1393 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1394 * height is off by 1 pixel which causes lengthy window relocations when
1395 * active document window is maximized/restored.
1397 * Calculate the size of the menu bar.
1399 static void FASTCALL
1400 MenuMenuBarCalcSize(HDC Dc
, LPRECT Rect
, PROSMENUINFO MenuInfo
, HWND WndOwner
)
1402 ROSMENUITEMINFO ItemInfo
;
1403 int Start
, i
, OrgX
, OrgY
, MaxY
, HelpPos
;
1405 if (NULL
== Rect
|| NULL
== MenuInfo
)
1409 if (0 == MenuInfo
->MenuItemCount
)
1414 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1415 Rect
->left
, Rect
->top
, Rect
->right
, Rect
->bottom
);
1416 MenuInfo
->Width
= Rect
->right
- Rect
->left
;
1417 MenuInfo
->Height
= 0;
1418 MaxY
= Rect
->top
+ 1;
1421 MenuInitRosMenuItemInfo(&ItemInfo
);
1422 while (Start
< MenuInfo
->MenuItemCount
)
1424 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1426 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1432 /* Parse items until line break or end of menu */
1433 for (i
= Start
; i
< MenuInfo
->MenuItemCount
; i
++)
1435 if (-1 == HelpPos
&& 0 != (ItemInfo
.fType
& MF_RIGHTJUSTIFY
))
1440 0 != (ItemInfo
.fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
)))
1445 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX
, OrgY
);
1446 MenuCalcItemSize(Dc
, &ItemInfo
, WndOwner
, OrgX
, OrgY
, TRUE
);
1447 if (! MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
))
1449 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1453 if (ItemInfo
.Rect
.right
> Rect
->right
)
1461 ItemInfo
.Rect
.right
= Rect
->right
;
1464 MaxY
= max(MaxY
, ItemInfo
.Rect
.bottom
);
1465 OrgX
= ItemInfo
.Rect
.right
;
1466 if (i
+ 1 < MenuInfo
->MenuItemCount
)
1468 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
+ 1, &ItemInfo
))
1470 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1476 /* FIXME: Is this really needed? */
1478 /* Finish the line (set all items to the largest height found) */
1481 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
))
1483 ItemInfo
.Rect
.bottom
= MaxY
;
1484 MenuSetRosMenuItemInfo(MenuInfo
->Self
, Start
, &ItemInfo
);
1493 Rect
->bottom
= MaxY
;
1494 MenuInfo
->Height
= Rect
->bottom
- Rect
->top
;
1495 MenuSetRosMenuInfo(MenuInfo
);
1499 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1500 /* the last item (if several lines, only move the last line) */
1501 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->MenuItemCount
- 1, &ItemInfo
))
1503 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1506 OrgY
= ItemInfo
.Rect
.top
;
1508 for (i
= MenuInfo
->MenuItemCount
- 1; HelpPos
<= i
; i
--)
1514 if (ItemInfo
.Rect
.top
!= OrgY
)
1516 break; /* Other line */
1518 if (OrgX
<= ItemInfo
.Rect
.right
)
1520 break; /* Too far right already */
1522 ItemInfo
.Rect
.left
+= OrgX
- ItemInfo
.Rect
.right
;
1523 ItemInfo
.Rect
.right
= OrgX
;
1524 OrgX
= ItemInfo
.Rect
.left
;
1525 MenuSetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
);
1526 if (HelpPos
+ 1 <= i
&&
1527 ! MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
- 1, &ItemInfo
))
1529 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1535 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1538 /***********************************************************************
1539 * DrawMenuBarTemp (USER32.@)
1543 * called by W98SE desk.cpl Control Panel Applet
1545 * Not 100% sure about the param names, but close.
1550 DrawMenuBarTemp(HWND Wnd
, HDC DC
, LPRECT Rect
, HMENU Menu
, HFONT Font
)
1552 ROSMENUINFO MenuInfo
;
1553 ROSMENUITEMINFO ItemInfo
;
1555 HFONT FontOld
= NULL
;
1559 Menu
= GetMenu(Wnd
);
1567 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1569 return GetSystemMetrics(SM_CYMENU
);
1572 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd
, DC
, Rect
, Menu
, Font
);
1574 FontOld
= SelectObject(DC
, Font
);
1576 if (0 == MenuInfo
.Height
)
1578 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1581 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1583 FillRect(DC
, Rect
, GetSysColorBrush(COLOR_MENU
));
1585 SelectObject(DC
, GetSysColorPen(COLOR_3DFACE
));
1586 MoveToEx(DC
, Rect
->left
, Rect
->bottom
, NULL
);
1587 LineTo(DC
, Rect
->right
, Rect
->bottom
);
1589 if (0 == MenuInfo
.MenuItemCount
)
1591 SelectObject(DC
, FontOld
);
1592 return GetSystemMetrics(SM_CYMENU
);
1595 MenuInitRosMenuItemInfo(&ItemInfo
);
1596 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1598 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1600 MenuDrawMenuItem(Wnd
, &MenuInfo
, Wnd
, DC
, &ItemInfo
,
1601 MenuInfo
.Height
, TRUE
, ODA_DRAWENTIRE
);
1604 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1606 SelectObject(DC
, FontOld
);
1608 return MenuInfo
.Height
;
1612 /***********************************************************************
1615 * Paint a menu bar. Returns the height of the menu bar.
1616 * called from [windows/nonclient.c]
1618 UINT
MenuDrawMenuBar(HDC DC
, LPRECT Rect
, HWND Wnd
, BOOL SuppressDraw
)
1620 ROSMENUINFO MenuInfo
;
1621 HFONT FontOld
= NULL
;
1622 HMENU Menu
= GetMenu(Wnd
);
1624 if (NULL
== Rect
|| ! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1626 return GetSystemMetrics(SM_CYMENU
);
1631 FontOld
= SelectObject(DC
, hMenuFont
);
1633 MenuMenuBarCalcSize(DC
, Rect
, &MenuInfo
, Wnd
);
1635 Rect
->bottom
= Rect
->top
+ MenuInfo
.Height
;
1637 if (NULL
!= FontOld
)
1639 SelectObject(DC
, FontOld
);
1641 return MenuInfo
.Height
;
1645 return DrawMenuBarTemp(Wnd
, DC
, Rect
, Menu
, NULL
);
1649 /***********************************************************************
1652 static BOOL FASTCALL
1653 MenuInitTracking(HWND Wnd
, HMENU Menu
, BOOL Popup
, UINT Flags
)
1655 DPRINT("Wnd=%p Menu=%p\n", Wnd
, Menu
);
1659 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1660 if (0 == (Flags
& TPM_NONOTIFY
))
1662 SendMessageW(Wnd
, WM_ENTERMENULOOP
, Popup
, 0);
1665 SendMessageW(Wnd
, WM_SETCURSOR
, (WPARAM
) Wnd
, HTCAPTION
);
1667 if (0 == (Flags
& TPM_NONOTIFY
))
1669 ROSMENUINFO MenuInfo
;
1671 SendMessageW(Wnd
, WM_INITMENU
, (WPARAM
)Menu
, 0);
1673 if (MenuGetRosMenuInfo(&MenuInfo
, Menu
) && 0 == MenuInfo
.Height
)
1675 /* app changed/recreated menu bar entries in WM_INITMENU
1676 Recalculate menu sizes else clicks will not work */
1677 SetWindowPos(Wnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
1678 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
1687 /***********************************************************************
1690 * Display a popup menu.
1692 static BOOL FASTCALL
1693 MenuShowPopup(HWND WndOwner
, HMENU Menu
, UINT Id
,
1694 INT X
, INT Y
, INT XAnchor
, INT YAnchor
)
1696 ROSMENUINFO MenuInfo
;
1697 ROSMENUITEMINFO ItemInfo
;
1700 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1701 WndOwner
, Menu
, Id
, X
, Y
, XAnchor
, YAnchor
);
1703 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
1708 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
1710 MenuInitRosMenuItemInfo(&ItemInfo
);
1711 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
1713 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1714 MenuSetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
);
1716 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1717 MenuInfo
.FocusedItem
= NO_SELECTED_ITEM
;
1720 /* store the owner for DrawItem */
1721 MenuInfo
.WndOwner
= WndOwner
;
1722 MenuSetRosMenuInfo(&MenuInfo
);
1724 MenuPopupMenuCalcSize(&MenuInfo
, WndOwner
);
1726 /* adjust popup menu pos so that it fits within the desktop */
1728 Width
= MenuInfo
.Width
+ GetSystemMetrics(SM_CXBORDER
);
1729 Height
= MenuInfo
.Height
+ GetSystemMetrics(SM_CYBORDER
);
1731 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1735 X
-= Width
- XAnchor
;
1737 if (GetSystemMetrics(SM_CXSCREEN
) < X
+ Width
)
1739 X
= GetSystemMetrics(SM_CXSCREEN
) - Width
;
1747 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1751 Y
-= Height
+ YAnchor
;
1753 if (GetSystemMetrics(SM_CYSCREEN
) < Y
+ Height
)
1755 Y
= GetSystemMetrics(SM_CYSCREEN
) - Height
;
1764 /* NOTE: In Windows, top menu popup is not owned. */
1765 MenuInfo
.Wnd
= CreateWindowExW(0, POPUPMENU_CLASS_ATOMW
, NULL
,
1766 WS_POPUP
, X
, Y
, Width
, Height
,
1767 WndOwner
, 0, (HINSTANCE
) GetWindowLongW(WndOwner
, GWL_HINSTANCE
),
1768 (LPVOID
) MenuInfo
.Self
);
1769 if (NULL
== MenuInfo
.Wnd
|| ! MenuSetRosMenuInfo(&MenuInfo
))
1773 if (NULL
== TopPopup
)
1775 TopPopup
= MenuInfo
.Wnd
;
1778 /* Display the window */
1779 SetWindowPos(MenuInfo
.Wnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1780 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1781 UpdateWindow(MenuInfo
.Wnd
);
1786 /***********************************************************************
1789 * Find a Sub menu. Return the position of the submenu, and modifies
1790 * *hmenu in case it is found in another sub-menu.
1791 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1793 static UINT FASTCALL
1794 MenuFindSubMenu(HMENU
*Menu
, HMENU SubTarget
)
1796 ROSMENUINFO MenuInfo
;
1797 ROSMENUITEMINFO ItemInfo
;
1802 if ((HMENU
) 0xffff == *Menu
1803 || ! MenuGetRosMenuInfo(&MenuInfo
, *Menu
))
1805 return NO_SELECTED_ITEM
;
1808 MenuInitRosMenuItemInfo(&ItemInfo
);
1809 for (i
= 0; i
< MenuInfo
.MenuItemCount
; i
++)
1811 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, i
, &ItemInfo
))
1813 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1814 return NO_SELECTED_ITEM
;
1816 if (0 == (ItemInfo
.fType
& MF_POPUP
))
1820 if (ItemInfo
.hSubMenu
== SubTarget
)
1822 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1825 SubMenu
= ItemInfo
.hSubMenu
;
1826 Pos
= MenuFindSubMenu(&SubMenu
, SubTarget
);
1827 if (NO_SELECTED_ITEM
!= Pos
)
1833 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1835 return NO_SELECTED_ITEM
;
1838 /***********************************************************************
1841 static void FASTCALL
1842 MenuSelectItem(HWND WndOwner
, PROSMENUINFO MenuInfo
, UINT Index
,
1843 BOOL SendMenuSelect
, HMENU TopMenu
)
1846 ROSMENUITEMINFO ItemInfo
;
1847 ROSMENUINFO TopMenuInfo
;
1850 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner
, MenuInfo
, Index
, SendMenuSelect
);
1852 if (NULL
== MenuInfo
|| 0 == MenuInfo
->MenuItemCount
|| NULL
== MenuInfo
->Wnd
)
1857 if (MenuInfo
->FocusedItem
== Index
)
1862 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
1864 Dc
= GetDC(MenuInfo
->Wnd
);
1868 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1871 if (NULL
== TopPopup
)
1873 TopPopup
= MenuInfo
->Wnd
;
1876 SelectObject(Dc
, hMenuFont
);
1877 MenuInitRosMenuItemInfo(&ItemInfo
);
1879 /* Clear previous highlighted item */
1880 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1882 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1884 ItemInfo
.fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1885 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1887 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
,
1888 MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1892 /* Highlight new item (if any) */
1893 MenuInfo
->FocusedItem
= Index
;
1894 MenuSetRosMenuInfo(MenuInfo
);
1895 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1897 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
1899 if (0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1901 ItemInfo
.fState
|= MF_HILITE
;
1902 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
1903 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
,
1904 &ItemInfo
, MenuInfo
->Height
, ! (MenuInfo
->Flags
& MF_POPUP
),
1909 SendMessageW(WndOwner
, WM_MENUSELECT
,
1910 MAKELONG(ItemInfo
.fType
& MF_POPUP
? Index
: ItemInfo
.wID
,
1911 ItemInfo
.fType
| ItemInfo
.fState
| MF_MOUSESELECT
|
1912 (MenuInfo
->Flags
& MF_SYSMENU
)), (LPARAM
) MenuInfo
->Self
);
1916 else if (SendMenuSelect
)
1918 if (NULL
!= TopMenu
)
1920 Pos
= MenuFindSubMenu(&TopMenu
, MenuInfo
->Self
);
1921 if (NO_SELECTED_ITEM
!= Pos
)
1923 if (MenuGetRosMenuInfo(&TopMenuInfo
, TopMenu
)
1924 && MenuGetRosMenuItemInfo(TopMenu
, Pos
, &ItemInfo
))
1926 SendMessageW(WndOwner
, WM_MENUSELECT
,
1927 MAKELONG(Pos
, ItemInfo
.fType
| ItemInfo
.fState
1929 | (TopMenuInfo
.Flags
& MF_SYSMENU
)),
1936 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1937 ReleaseDC(MenuInfo
->Wnd
, Dc
);
1940 /***********************************************************************
1943 * Moves currently selected item according to the Offset parameter.
1944 * If there is no selection then it should select the last item if
1945 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1947 static void FASTCALL
1948 MenuMoveSelection(HWND WndOwner
, PROSMENUINFO MenuInfo
, INT Offset
)
1951 ROSMENUITEMINFO ItemInfo
;
1953 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner
, MenuInfo
, Offset
);
1955 MenuInitRosMenuItemInfo(&ItemInfo
);
1956 if (NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
1958 if (1 == MenuInfo
->MenuItemCount
)
1960 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1965 for (i
= MenuInfo
->FocusedItem
+ Offset
;
1966 0 <= i
&& i
< MenuInfo
->MenuItemCount
;
1969 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1970 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1972 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1973 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1980 for (i
= (0 < Offset
) ? 0 : MenuInfo
->MenuItemCount
- 1;
1981 0 <= i
&& i
< MenuInfo
->MenuItemCount
; i
+= Offset
)
1983 if (MenuGetRosMenuItemInfo(MenuInfo
->Self
, i
, &ItemInfo
) &&
1984 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
1986 MenuSelectItem(WndOwner
, MenuInfo
, i
, TRUE
, NULL
);
1987 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1992 MenuCleanupRosMenuItemInfo(&ItemInfo
);
1995 /***********************************************************************
1996 * MenuInitSysMenuPopup
1998 * Grey the appropriate items in System menu.
2001 MenuInitSysMenuPopup(HMENU Menu
, DWORD Style
, DWORD ClsStyle
, LONG HitTest
)
2009 Gray
= 0 == (Style
& WS_THICKFRAME
) || 0 != (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
2010 EnableMenuItem(Menu
, SC_SIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2011 Gray
= 0 != (Style
& WS_MAXIMIZE
);
2012 EnableMenuItem(Menu
, SC_MOVE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2013 Gray
= 0 == (Style
& WS_MINIMIZEBOX
) || 0 != (Style
& WS_MINIMIZE
);
2014 EnableMenuItem(Menu
, SC_MINIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2015 Gray
= 0 == (Style
& WS_MAXIMIZEBOX
) || 0 != (Style
& WS_MAXIMIZE
);
2016 EnableMenuItem(Menu
, SC_MAXIMIZE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2017 Gray
= 0 == (Style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
2018 EnableMenuItem(Menu
, SC_RESTORE
, (Gray
? MF_GRAYED
: MF_ENABLED
));
2019 Gray
= 0 != (ClsStyle
& CS_NOCLOSE
);
2021 /* The menu item must keep its state if it's disabled */
2024 EnableMenuItem(Menu
, SC_CLOSE
, MF_GRAYED
);
2027 /* Set default menu item */
2028 if(Style
& WS_MINIMIZE
)
2030 DefItem
= SC_RESTORE
;
2034 if(HitTest
== HTCAPTION
)
2036 DefItem
= ((Style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
2044 mii
.cbSize
= sizeof(MENUITEMINFOW
);
2045 mii
.fMask
= MIIM_STATE
;
2046 if((DefItem
!= SC_CLOSE
) && GetMenuItemInfoW(Menu
, DefItem
, FALSE
, &mii
) &&
2047 (mii
.fState
& (MFS_GRAYED
| MFS_DISABLED
)))
2052 SetMenuDefaultItem(Menu
, DefItem
, MF_BYCOMMAND
);
2055 /***********************************************************************
2058 * Display the sub-menu of the selected item of this menu.
2059 * Return the handle of the submenu, or menu if no submenu to display.
2061 static HMENU FASTCALL
2062 MenuShowSubPopup(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SelectFirst
, UINT Flags
)
2064 extern void FASTCALL
NcGetSysPopupPos(HWND Wnd
, RECT
*Rect
);
2066 ROSMENUITEMINFO ItemInfo
;
2067 ROSMENUINFO SubMenuInfo
;
2071 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner
, MenuInfo
, SelectFirst
);
2073 if (NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2075 return MenuInfo
->Self
;
2078 MenuInitRosMenuItemInfo(&ItemInfo
);
2079 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2081 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2082 return MenuInfo
->Self
;
2084 if (0 == (ItemInfo
.fType
& MF_POPUP
) || 0 != (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
)))
2086 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2087 return MenuInfo
->Self
;
2090 /* message must be sent before using item,
2091 because nearly everything may be changed by the application ! */
2093 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2094 if (0 == (Flags
& TPM_NONOTIFY
))
2096 SendMessageW(WndOwner
, WM_INITMENUPOPUP
, (WPARAM
) ItemInfo
.hSubMenu
,
2097 MAKELONG(MenuInfo
->FocusedItem
, IS_SYSTEM_MENU(MenuInfo
)));
2100 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2102 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2103 return MenuInfo
->Self
;
2105 Rect
= ItemInfo
.Rect
;
2107 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2108 if (0 == (ItemInfo
.fState
& MF_HILITE
))
2110 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2112 Dc
= GetDC(MenuInfo
->Wnd
);
2116 Dc
= GetDCEx(MenuInfo
->Wnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2119 SelectObject(Dc
, hMenuFont
);
2121 ItemInfo
.fState
|= MF_HILITE
;
2122 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2123 MenuDrawMenuItem(MenuInfo
->Wnd
, MenuInfo
, WndOwner
, Dc
, &ItemInfo
, MenuInfo
->Height
,
2124 ! (MenuInfo
->Flags
& MF_POPUP
), ODA_DRAWENTIRE
);
2125 ReleaseDC(MenuInfo
->Wnd
, Dc
);
2128 if (0 == ItemInfo
.Rect
.top
&& 0 == ItemInfo
.Rect
.left
2129 && 0 == ItemInfo
.Rect
.bottom
&& 0 == ItemInfo
.Rect
.right
)
2131 ItemInfo
.Rect
= Rect
;
2134 ItemInfo
.fState
|= MF_MOUSESELECT
;
2136 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2138 if (IS_SYSTEM_MENU(MenuInfo
))
2140 MenuInitSysMenuPopup(ItemInfo
.hSubMenu
, GetWindowLongW(MenuInfo
->Wnd
, GWL_STYLE
),
2141 GetClassLongW(MenuInfo
->Wnd
, GCL_STYLE
), HTSYSMENU
);
2143 NcGetSysPopupPos(MenuInfo
->Wnd
, &Rect
);
2144 Rect
.top
= Rect
.bottom
;
2145 Rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2146 Rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2150 GetWindowRect(MenuInfo
->Wnd
, &Rect
);
2151 if (0 != (MenuInfo
->Flags
& MF_POPUP
))
2153 Rect
.left
+= ItemInfo
.Rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2154 Rect
.top
+= ItemInfo
.Rect
.top
;
2155 Rect
.right
= ItemInfo
.Rect
.left
- ItemInfo
.Rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2156 Rect
.bottom
= ItemInfo
.Rect
.top
- ItemInfo
.Rect
.bottom
;
2160 Rect
.left
+= ItemInfo
.Rect
.left
;
2161 Rect
.top
+= ItemInfo
.Rect
.bottom
;
2162 Rect
.right
= ItemInfo
.Rect
.right
- ItemInfo
.Rect
.left
;
2163 Rect
.bottom
= ItemInfo
.Rect
.bottom
- ItemInfo
.Rect
.top
;
2167 MenuShowPopup(WndOwner
, ItemInfo
.hSubMenu
, MenuInfo
->FocusedItem
,
2168 Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
);
2169 if (SelectFirst
&& MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2171 MenuMoveSelection(WndOwner
, &SubMenuInfo
, ITEM_NEXT
);
2174 Ret
= ItemInfo
.hSubMenu
;
2175 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2180 /***********************************************************************
2183 * Hide the sub-popup menus of this menu.
2185 static void FASTCALL
2186 MenuHideSubPopups(HWND WndOwner
, PROSMENUINFO MenuInfo
, BOOL SendMenuSelect
)
2188 ROSMENUINFO SubMenuInfo
;
2189 ROSMENUITEMINFO ItemInfo
;
2191 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner
, MenuInfo
, SendMenuSelect
);
2193 if (NULL
!= MenuInfo
&& NULL
!= TopPopup
&& NO_SELECTED_ITEM
!= MenuInfo
->FocusedItem
)
2195 MenuInitRosMenuItemInfo(&ItemInfo
);
2196 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
)
2197 || 0 == (ItemInfo
.fType
& MF_POPUP
)
2198 || 0 == (ItemInfo
.fState
& MF_MOUSESELECT
))
2200 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2203 ItemInfo
.fState
&= ~MF_MOUSESELECT
;
2204 MenuSetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
);
2205 if (MenuGetRosMenuInfo(&SubMenuInfo
, ItemInfo
.hSubMenu
))
2207 MenuHideSubPopups(WndOwner
, &SubMenuInfo
, FALSE
);
2208 MenuSelectItem(WndOwner
, &SubMenuInfo
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
2209 DestroyWindow(SubMenuInfo
.Wnd
);
2210 SubMenuInfo
.Wnd
= NULL
;
2211 MenuSetRosMenuInfo(&SubMenuInfo
);
2216 /***********************************************************************
2217 * MenuSwitchTracking
2219 * Helper function for menu navigation routines.
2221 static void FASTCALL
2222 MenuSwitchTracking(MTRACKER
* Mt
, PROSMENUINFO PtMenuInfo
, UINT Index
)
2224 ROSMENUINFO TopMenuInfo
;
2226 DPRINT("%x menu=%x 0x%04x\n", Mt
, PtMenuInfo
->Self
, Index
);
2228 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
) &&
2229 Mt
->TopMenu
!= PtMenuInfo
->Self
&&
2230 0 == ((PtMenuInfo
->Flags
| TopMenuInfo
.Flags
) & MF_POPUP
))
2232 /* both are top level menus (system and menu-bar) */
2233 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2234 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
2235 Mt
->TopMenu
= PtMenuInfo
->Self
;
2239 MenuHideSubPopups(Mt
->OwnerWnd
, PtMenuInfo
, FALSE
);
2242 MenuSelectItem(Mt
->OwnerWnd
, PtMenuInfo
, Index
, TRUE
, NULL
);
2245 /***********************************************************************
2246 * MenuExecFocusedItem
2248 * Execute a menu item (for instance when user pressed Enter).
2249 * Return the wID of the executed item. Otherwise, -1 indicating
2250 * that no menu item was executed;
2251 * Have to receive the flags for the TrackPopupMenu options to avoid
2252 * sending unwanted message.
2256 MenuExecFocusedItem(MTRACKER
*Mt
, PROSMENUINFO MenuInfo
, UINT Flags
)
2258 ROSMENUITEMINFO ItemInfo
;
2261 DPRINT("%p menu=%p\n", Mt
, MenuInfo
);
2263 if (0 == MenuInfo
->MenuItemCount
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2268 MenuInitRosMenuItemInfo(&ItemInfo
);
2269 if (! MenuGetRosMenuItemInfo(MenuInfo
->Self
, MenuInfo
->FocusedItem
, &ItemInfo
))
2271 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2275 DPRINT("%p %08x %p\n", MenuInfo
, ItemInfo
.wID
, ItemInfo
.hSubMenu
);
2277 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2279 if (0 == (ItemInfo
.fState
& (MF_GRAYED
| MF_DISABLED
))
2280 && 0 == (ItemInfo
.fType
& MF_SEPARATOR
))
2282 /* If TPM_RETURNCMD is set you return the id, but
2283 do not send a message to the owner */
2284 if (0 == (Flags
& TPM_RETURNCMD
))
2286 if (0 != (MenuInfo
->Flags
& MF_SYSMENU
))
2288 PostMessageW(Mt
->OwnerWnd
, WM_SYSCOMMAND
, ItemInfo
.wID
,
2289 MAKELPARAM((SHORT
) Mt
->Pt
.x
, (SHORT
) Mt
->Pt
.y
));
2293 PostMessageW(Mt
->OwnerWnd
, WM_COMMAND
, ItemInfo
.wID
, 0);
2297 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2303 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, MenuInfo
, TRUE
, Flags
);
2309 /***********************************************************************
2312 * Return TRUE if we can go on with menu tracking.
2314 static BOOL FASTCALL
2315 MenuButtonDown(MTRACKER
* Mt
, HMENU PtMenu
, UINT Flags
)
2318 ROSMENUINFO MenuInfo
;
2319 ROSMENUITEMINFO Item
;
2321 DPRINT("%x PtMenu=%p\n", Mt
, PtMenu
);
2325 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2329 if (IS_SYSTEM_MENU(&MenuInfo
))
2335 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2337 MenuInitRosMenuItemInfo(&Item
);
2338 if (NO_SELECTED_ITEM
== Index
|| ! MenuGetRosMenuItemInfo(PtMenu
, Index
, &Item
))
2340 MenuCleanupRosMenuItemInfo(&Item
);
2344 if (MenuInfo
.FocusedItem
!= Index
)
2346 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2349 /* If the popup menu is not already "popped" */
2350 if (0 == (Item
.fState
& MF_MOUSESELECT
))
2352 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2355 MenuCleanupRosMenuItemInfo(&Item
);
2360 /* else the click was on the menu bar, finish the tracking */
2365 /***********************************************************************
2368 * Return the value of MenuExecFocusedItem if
2369 * the selected item was not a popup. Else open the popup.
2370 * A -1 return value indicates that we go on with menu tracking.
2374 MenuButtonUp(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2377 ROSMENUINFO MenuInfo
;
2378 ROSMENUITEMINFO ItemInfo
;
2380 DPRINT("%p hmenu=%x\n", Mt
, PtMenu
);
2385 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2390 if (! IS_SYSTEM_MENU(&MenuInfo
))
2392 Id
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, MenuInfo
.Self
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2394 MenuInitRosMenuItemInfo(&ItemInfo
);
2395 if (0 <= Id
&& MenuGetRosMenuItemInfo(MenuInfo
.Self
, Id
, &ItemInfo
) &&
2396 MenuInfo
.FocusedItem
== Id
)
2398 if (0 == (ItemInfo
.fType
& MF_POPUP
))
2400 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2401 return MenuExecFocusedItem(Mt
, &MenuInfo
, Flags
);
2403 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2405 /* If we are dealing with the top-level menu */
2406 /* and this is a click on an already "popped" item: */
2407 /* Stop the menu tracking and close the opened submenus */
2408 if (Mt
->TopMenu
== MenuInfo
.Self
&& MenuInfo
.TimeToHide
)
2410 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2414 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2415 MenuInfo
.TimeToHide
= TRUE
;
2416 MenuSetRosMenuInfo(&MenuInfo
);
2422 /***********************************************************************
2425 * Walks menu chain trying to find a menu pt maps to.
2427 static HMENU FASTCALL
2428 MenuPtMenu(HMENU Menu
, POINT Pt
)
2430 extern LRESULT
DefWndNCHitTest(HWND hWnd
, POINT Point
);
2431 ROSMENUINFO MenuInfo
;
2432 ROSMENUITEMINFO ItemInfo
;
2436 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
2441 /* try subpopup first (if any) */
2442 if (NO_SELECTED_ITEM
!= MenuInfo
.FocusedItem
)
2444 MenuInitRosMenuItemInfo(&ItemInfo
);
2445 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
) &&
2446 0 != (ItemInfo
.fType
& MF_POPUP
) &&
2447 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2449 Ret
= MenuPtMenu(ItemInfo
.hSubMenu
, Pt
);
2452 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2456 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2459 /* check the current window (avoiding WM_HITTEST) */
2460 Ht
= DefWndNCHitTest(MenuInfo
.Wnd
, Pt
);
2461 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
2463 if (HTNOWHERE
!= Ht
&& HTERROR
!= Ht
)
2468 else if (HTSYSMENU
== Ht
)
2470 Ret
= NtUserGetSystemMenu(MenuInfo
.Wnd
, FALSE
);
2472 else if (HTMENU
== Ht
)
2474 Ret
= GetMenu(MenuInfo
.Wnd
);
2480 /***********************************************************************
2483 * Return TRUE if we can go on with menu tracking.
2485 static BOOL FASTCALL
2486 MenuMouseMove(MTRACKER
*Mt
, HMENU PtMenu
, UINT Flags
)
2489 ROSMENUINFO MenuInfo
;
2493 if (! MenuGetRosMenuInfo(&MenuInfo
, PtMenu
))
2497 if (IS_SYSTEM_MENU(&MenuInfo
))
2503 Index
= NtUserMenuItemFromPoint(Mt
->OwnerWnd
, PtMenu
, Mt
->Pt
.x
, Mt
->Pt
.y
);
2508 Index
= NO_SELECTED_ITEM
;
2511 if (NO_SELECTED_ITEM
== Index
)
2513 if (Mt
->CurrentMenu
== MenuInfo
.Self
||
2514 MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2516 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
2520 else if (MenuInfo
.FocusedItem
!= Index
)
2522 MenuSwitchTracking(Mt
, &MenuInfo
, Index
);
2523 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
, FALSE
, Flags
);
2529 /******************************************************************************
2531 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2533 static UINT
MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo
)
2536 PROSMENUITEMINFO MenuItems
;
2538 i
= MenuInfo
->FocusedItem
;
2539 if (NO_SELECTED_ITEM
== i
)
2544 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2546 return NO_SELECTED_ITEM
;
2549 for (i
++ ; i
< MenuInfo
->MenuItemCount
; i
++)
2551 if (0 != (MenuItems
[i
].fType
& MF_MENUBARBREAK
))
2557 return NO_SELECTED_ITEM
;
2560 /******************************************************************************
2562 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2564 static UINT FASTCALL
2565 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo
)
2568 PROSMENUITEMINFO MenuItems
;
2570 if (0 == MenuInfo
->FocusedItem
|| NO_SELECTED_ITEM
== MenuInfo
->FocusedItem
)
2572 return NO_SELECTED_ITEM
;
2575 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &MenuItems
) <= 0)
2577 return NO_SELECTED_ITEM
;
2580 /* Find the start of the column */
2582 for (i
= MenuInfo
->FocusedItem
;
2583 0 != i
&& 0 == (MenuItems
[i
].fType
& MF_MENUBARBREAK
);
2591 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2592 return NO_SELECTED_ITEM
;
2595 for (--i
; 0 != i
; --i
)
2597 if (MenuItems
[i
].fType
& MF_MENUBARBREAK
)
2603 MenuCleanupAllRosMenuItemInfo(MenuItems
);
2604 DPRINT("ret %d.\n", i
);
2609 /***********************************************************************
2612 * Return the handle of the selected sub-popup menu (if any).
2614 static HMENU FASTCALL
2615 MenuGetSubPopup(HMENU Menu
)
2617 ROSMENUINFO MenuInfo
;
2618 ROSMENUITEMINFO ItemInfo
;
2620 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
)
2621 || NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
2626 MenuInitRosMenuItemInfo(&ItemInfo
);
2627 if (! MenuGetRosMenuItemInfo(MenuInfo
.Self
, MenuInfo
.FocusedItem
, &ItemInfo
))
2629 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2632 if (0 != (ItemInfo
.fType
& MF_POPUP
) && 0 != (ItemInfo
.fState
& MF_MOUSESELECT
))
2634 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2635 return ItemInfo
.hSubMenu
;
2638 MenuCleanupRosMenuItemInfo(&ItemInfo
);
2642 /***********************************************************************
2645 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2647 static LRESULT FASTCALL
2648 MenuDoNextMenu(MTRACKER
* Mt
, UINT Vk
)
2650 ROSMENUINFO TopMenuInfo
;
2651 ROSMENUINFO MenuInfo
;
2653 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2655 return (LRESULT
) FALSE
;
2658 if ((VK_LEFT
== Vk
&& 0 == TopMenuInfo
.FocusedItem
)
2659 || (VK_RIGHT
== Vk
&& TopMenuInfo
.FocusedItem
== TopMenuInfo
.MenuItemCount
- 1))
2661 MDINEXTMENU NextMenu
;
2666 NextMenu
.hmenuIn
= (IS_SYSTEM_MENU(&TopMenuInfo
)) ? GetSubMenu(Mt
->TopMenu
, 0) : Mt
->TopMenu
;
2667 NextMenu
.hmenuNext
= NULL
;
2668 NextMenu
.hwndNext
= NULL
;
2669 SendMessageW(Mt
->OwnerWnd
, WM_NEXTMENU
, Vk
, (LPARAM
) &NextMenu
);
2671 DPRINT("%p [%p] -> %p [%p]\n",
2672 Mt
->CurrentMenu
, Mt
->OwnerWnd
, NextMenu
.hmenuNext
, NextMenu
.hwndNext
);
2674 if (NULL
== NextMenu
.hmenuNext
|| NULL
== NextMenu
.hwndNext
)
2676 DWORD Style
= GetWindowLongW(Mt
->OwnerWnd
, GWL_STYLE
);
2677 NewWnd
= Mt
->OwnerWnd
;
2678 if (IS_SYSTEM_MENU(&TopMenuInfo
))
2680 /* switch to the menu bar */
2682 if (0 != (Style
& WS_CHILD
)
2683 || NULL
== (NewMenu
= GetMenu(NewWnd
)))
2690 if (! MenuGetRosMenuInfo(&MenuInfo
, NewMenu
))
2694 Id
= MenuInfo
.MenuItemCount
- 1;
2697 else if (0 != (Style
& WS_SYSMENU
))
2699 /* switch to the system menu */
2700 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2707 else /* application returned a new menu to switch to */
2709 NewMenu
= NextMenu
.hmenuNext
;
2710 NewWnd
= NextMenu
.hwndNext
;
2712 if (IsMenu(NewMenu
) && IsWindow(NewWnd
))
2714 DWORD Style
= GetWindowLongW(NewWnd
, GWL_STYLE
);
2716 if (0 != (Style
& WS_SYSMENU
)
2717 && GetSystemMenu(NewWnd
, FALSE
) == NewMenu
)
2719 /* get the real system menu */
2720 NewMenu
= NtUserGetSystemMenu(NewWnd
, FALSE
);
2722 else if (0 != (Style
& WS_CHILD
) || GetMenu(NewWnd
) != NewMenu
)
2724 /* FIXME: Not sure what to do here;
2725 * perhaps try to track NewMenu as a popup? */
2727 DPRINT(" -- got confused.\n");
2737 if (NewMenu
!= Mt
->TopMenu
)
2739 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, NO_SELECTED_ITEM
,
2741 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2743 MenuHideSubPopups(Mt
->OwnerWnd
, &TopMenuInfo
, FALSE
);
2747 if (NewWnd
!= Mt
->OwnerWnd
)
2749 Mt
->OwnerWnd
= NewWnd
;
2750 SetCapture(Mt
->OwnerWnd
);
2751 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
->OwnerWnd
);
2754 Mt
->TopMenu
= Mt
->CurrentMenu
= NewMenu
; /* all subpopups are hidden */
2755 if (MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2757 MenuSelectItem(Mt
->OwnerWnd
, &TopMenuInfo
, Id
, TRUE
, 0);
2766 /***********************************************************************
2769 * The idea is not to show the popup if the next input message is
2770 * going to hide it anyway.
2772 static BOOL FASTCALL
2773 MenuSuspendPopup(MTRACKER
* Mt
, UINT Message
)
2777 Msg
.hwnd
= Mt
->OwnerWnd
;
2779 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2780 Mt
->TrackFlags
|= TF_SKIPREMOVE
;
2785 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2786 if (WM_KEYUP
== Msg
.message
|| WM_PAINT
== Msg
.message
)
2788 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2789 PeekMessageW(&Msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2790 if (WM_KEYDOWN
== Msg
.message
2791 && (VK_LEFT
== Msg
.wParam
|| VK_RIGHT
== Msg
.wParam
))
2793 Mt
->TrackFlags
|= TF_SUSPENDPOPUP
;
2800 /* failures go through this */
2801 Mt
->TrackFlags
&= ~TF_SUSPENDPOPUP
;
2806 /***********************************************************************
2809 * Handle a VK_ESCAPE key event in a menu.
2811 static BOOL FASTCALL
2812 MenuKeyEscape(MTRACKER
*Mt
, UINT Flags
)
2814 BOOL EndMenu
= TRUE
;
2815 ROSMENUINFO MenuInfo
;
2816 HMENU MenuTmp
, MenuPrev
;
2818 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2820 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
)
2821 && 0 != (MenuInfo
.Flags
& MF_POPUP
))
2823 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2825 /* close topmost popup */
2826 while (MenuTmp
!= Mt
->CurrentMenu
)
2829 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2832 if (MenuGetRosMenuInfo(&MenuInfo
, MenuPrev
))
2834 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, TRUE
);
2836 Mt
->CurrentMenu
= MenuPrev
;
2844 /***********************************************************************
2847 * Handle a VK_LEFT key event in a menu.
2849 static void FASTCALL
2850 MenuKeyLeft(MTRACKER
* Mt
, UINT Flags
)
2852 ROSMENUINFO MenuInfo
;
2853 ROSMENUINFO TopMenuInfo
;
2854 ROSMENUINFO PrevMenuInfo
;
2855 HMENU MenuTmp
, MenuPrev
;
2858 MenuPrev
= MenuTmp
= Mt
->TopMenu
;
2860 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2865 /* Try to move 1 column left (if possible) */
2866 if (NO_SELECTED_ITEM
!= (PrevCol
= MenuGetStartOfPrevColumn(&MenuInfo
)))
2868 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2870 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, PrevCol
, TRUE
, 0);
2875 /* close topmost popup */
2876 while (MenuTmp
!= Mt
->CurrentMenu
)
2879 MenuTmp
= MenuGetSubPopup(MenuPrev
);
2882 if (! MenuGetRosMenuInfo(&PrevMenuInfo
, MenuPrev
))
2886 MenuHideSubPopups(Mt
->OwnerWnd
, &PrevMenuInfo
, TRUE
);
2887 Mt
->CurrentMenu
= MenuPrev
;
2889 if (! MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2893 if ((MenuPrev
== Mt
->TopMenu
) && 0 == (TopMenuInfo
.Flags
& MF_POPUP
))
2895 /* move menu bar selection if no more popups are left */
2897 if (! MenuDoNextMenu(Mt
, VK_LEFT
))
2899 MenuMoveSelection(Mt
->OwnerWnd
, &TopMenuInfo
, ITEM_PREV
);
2902 if (MenuPrev
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2904 /* A sublevel menu was displayed - display the next one
2905 * unless there is another displacement coming up */
2907 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2908 && MenuGetRosMenuInfo(&TopMenuInfo
, Mt
->TopMenu
))
2910 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &TopMenuInfo
,
2917 /***********************************************************************
2920 * Handle a VK_RIGHT key event in a menu.
2922 static void FASTCALL
2923 MenuKeyRight(MTRACKER
*Mt
, UINT Flags
)
2926 ROSMENUINFO MenuInfo
;
2927 ROSMENUINFO CurrentMenuInfo
;
2930 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2931 Mt
->CurrentMenu
, Mt
->TopMenu
);
2933 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2937 if (0 != (MenuInfo
.Flags
& MF_POPUP
) || (Mt
->CurrentMenu
!= Mt
->TopMenu
))
2939 /* If already displaying a popup, try to display sub-popup */
2941 MenuTmp
= Mt
->CurrentMenu
;
2942 if (MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2944 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &CurrentMenuInfo
, TRUE
, Flags
);
2947 /* if subpopup was displayed then we are done */
2948 if (MenuTmp
!= Mt
->CurrentMenu
)
2954 if (! MenuGetRosMenuInfo(&CurrentMenuInfo
, Mt
->CurrentMenu
))
2959 /* Check to see if there's another column */
2960 if (NO_SELECTED_ITEM
!= (NextCol
= MenuGetStartOfNextColumn(&CurrentMenuInfo
)))
2962 DPRINT("Going to %d.\n", NextCol
);
2963 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
->CurrentMenu
))
2965 MenuSelectItem(Mt
->OwnerWnd
, &MenuInfo
, NextCol
, TRUE
, 0);
2970 if (0 == (MenuInfo
.Flags
& MF_POPUP
)) /* menu bar tracking */
2972 if (Mt
->CurrentMenu
!= Mt
->TopMenu
)
2974 MenuHideSubPopups(Mt
->OwnerWnd
, &MenuInfo
, FALSE
);
2975 MenuTmp
= Mt
->CurrentMenu
= Mt
->TopMenu
;
2982 /* try to move to the next item */
2983 if (! MenuDoNextMenu(Mt
, VK_RIGHT
))
2985 MenuMoveSelection(Mt
->OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
2988 if (NULL
!= MenuTmp
|| 0 != (Mt
->TrackFlags
& TF_SUSPENDPOPUP
))
2990 if (! MenuSuspendPopup(Mt
, WM_KEYDOWN
)
2991 && MenuGetRosMenuInfo(&MenuInfo
, Mt
->TopMenu
))
2993 Mt
->CurrentMenu
= MenuShowSubPopup(Mt
->OwnerWnd
, &MenuInfo
,
3000 /***********************************************************************
3003 * Find the menu item selected by a key press.
3004 * Return item id, -1 if none, -2 if we should close the menu.
3006 static UINT FASTCALL
3007 MenuFindItemByKey(HWND WndOwner
, PROSMENUINFO MenuInfo
,
3008 WCHAR Key
, BOOL ForceMenuChar
)
3010 ROSMENUINFO SysMenuInfo
;
3011 PROSMENUITEMINFO Items
, ItemInfo
;
3015 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key
, Key
, MenuInfo
);
3017 if (NULL
== MenuInfo
|| ! IsMenu(MenuInfo
->Self
))
3019 if (MenuGetRosMenuInfo(&SysMenuInfo
, GetSystemMenu(WndOwner
, FALSE
)))
3021 MenuInfo
= &SysMenuInfo
;
3029 if (NULL
!= MenuInfo
)
3031 if (MenuGetAllRosMenuItemInfo(MenuInfo
->Self
, &Items
) <= 0)
3035 if (! ForceMenuChar
)
3037 Key
= towupper(Key
);
3039 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++, ItemInfo
++)
3041 if (IS_STRING_ITEM(ItemInfo
->fType
) && NULL
!= ItemInfo
->dwTypeData
)
3043 WCHAR
*p
= (WCHAR
*) ItemInfo
->dwTypeData
- 2;
3046 p
= wcschr(p
+ 2, '&');
3048 while (NULL
!= p
&& L
'&' == p
[1]);
3049 if (NULL
!= p
&& (towupper(p
[1]) == Key
))
3057 MenuChar
= SendMessageW(WndOwner
, WM_MENUCHAR
,
3058 MAKEWPARAM(Key
, MenuInfo
->Flags
), (LPARAM
) MenuInfo
->Self
);
3059 if (2 == HIWORD(MenuChar
))
3061 return LOWORD(MenuChar
);
3063 if (1 == HIWORD(MenuChar
))
3072 /***********************************************************************
3075 * Menu tracking code.
3078 MenuTrackMenu(HMENU Menu
, UINT Flags
, INT x
, INT y
,
3079 HWND Wnd
, const RECT
*Rect
)
3082 ROSMENUINFO MenuInfo
;
3083 ROSMENUITEMINFO ItemInfo
;
3085 INT ExecutedMenuId
= -1;
3087 BOOL EnterIdleSent
= FALSE
;
3090 Mt
.CurrentMenu
= Menu
;
3096 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3097 Menu
, Flags
, x
, y
, Wnd
, Rect
? Rect
->left
: 0, Rect
? Rect
->top
: 0,
3098 Rect
? Rect
->right
: 0, Rect
? Rect
->bottom
: 0);
3101 if (! MenuGetRosMenuInfo(&MenuInfo
, Menu
))
3106 if (0 != (Flags
& TPM_BUTTONDOWN
))
3108 /* Get the result in order to start the tracking or not */
3109 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3110 fEndMenu
= ! fRemove
;
3113 SetCapture(Mt
.OwnerWnd
);
3114 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, Mt
.OwnerWnd
);
3118 /* we have to keep the message in the queue until it's
3119 * clear that menu loop is not over yet. */
3123 if (PeekMessageW(&Msg
, 0, 0, 0, PM_NOREMOVE
))
3125 if (! CallMsgFilterW(&Msg
, MSGF_MENU
))
3129 /* remove the message from the queue */
3130 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3134 if (! EnterIdleSent
)
3136 HWND Win
= (0 != (Flags
& TPM_ENTERIDLEEX
)
3137 && 0 != (MenuInfo
.Flags
& MF_POPUP
)) ? MenuInfo
.Wnd
: NULL
;
3138 EnterIdleSent
= TRUE
;
3139 SendMessageW(Mt
.OwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
) Win
);
3145 /* check if EndMenu() tried to cancel us, by posting this message */
3146 if (WM_CANCELMODE
== Msg
.message
)
3148 /* we are now out of the loop */
3151 /* remove the message from the queue */
3152 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3154 /* break out of internal loop, ala ESCAPE */
3158 TranslateMessage(&Msg
);
3161 if (Msg
.hwnd
== MenuInfo
.Wnd
|| WM_TIMER
!= Msg
.message
)
3163 EnterIdleSent
= FALSE
;
3167 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
3170 * Use the mouse coordinates in lParam instead of those in the MSG
3171 * struct to properly handle synthetic messages. They are already
3172 * in screen coordinates.
3174 Mt
.Pt
.x
= (short) LOWORD(Msg
.lParam
);
3175 Mt
.Pt
.y
= (short) HIWORD(Msg
.lParam
);
3177 /* Find a menu for this mouse event */
3178 Menu
= MenuPtMenu(Mt
.TopMenu
, Mt
.Pt
);
3182 /* no WM_NC... messages in captured state */
3184 case WM_RBUTTONDBLCLK
:
3185 case WM_RBUTTONDOWN
:
3186 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3191 case WM_LBUTTONDBLCLK
:
3192 case WM_LBUTTONDOWN
:
3193 /* If the message belongs to the menu, removes it from the queue */
3194 /* Else, end menu tracking */
3195 fRemove
= MenuButtonDown(&Mt
, Menu
, Flags
);
3196 fEndMenu
= ! fRemove
;
3200 if (0 == (Flags
& TPM_RIGHTBUTTON
))
3206 /* Check if a menu was selected by the mouse */
3209 ExecutedMenuId
= MenuButtonUp(&Mt
, Menu
, Flags
);
3211 /* End the loop if ExecutedMenuId is an item ID */
3212 /* or if the job was done (ExecutedMenuId = 0). */
3213 fEndMenu
= fRemove
= (-1 != ExecutedMenuId
);
3217 /* No menu was selected by the mouse */
3218 /* if the function was called by TrackPopupMenu, continue
3219 with the menu tracking. If not, stop it */
3220 fEndMenu
= (0 != (Flags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3227 fEndMenu
|= ! MenuMouseMove(&Mt
, Menu
, Flags
);
3231 } /* switch(Msg.message) - mouse */
3233 else if (WM_KEYFIRST
<= Msg
.message
&& Msg
.message
<= WM_KEYLAST
)
3235 fRemove
= TRUE
; /* Keyboard messages are always removed */
3243 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3245 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
,
3251 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3253 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
,
3254 VK_HOME
== Msg
.wParam
? ITEM_NEXT
: ITEM_PREV
);
3258 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3259 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3261 if (0 == (MenuInfo
.Flags
& MF_POPUP
))
3263 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3265 Mt
.CurrentMenu
= MenuShowSubPopup(Mt
.OwnerWnd
, &MenuInfo
,
3269 else /* otherwise try to move selection */
3271 MenuMoveSelection(Mt
.OwnerWnd
, &MenuInfo
, ITEM_NEXT
);
3277 MenuKeyLeft(&Mt
, Flags
);
3281 MenuKeyRight(&Mt
, Flags
);
3285 fEndMenu
= MenuKeyEscape(&Mt
, Flags
);
3291 hi
.cbSize
= sizeof(HELPINFO
);
3292 hi
.iContextType
= HELPINFO_MENUITEM
;
3293 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3295 if (NO_SELECTED_ITEM
== MenuInfo
.FocusedItem
)
3301 MenuInitRosMenuItemInfo(&ItemInfo
);
3302 if (MenuGetRosMenuItemInfo(MenuInfo
.Self
,
3303 MenuInfo
.FocusedItem
,
3306 hi
.iCtrlId
= ItemInfo
.wID
;
3312 MenuCleanupRosMenuItemInfo(&ItemInfo
);
3315 hi
.hItemHandle
= Menu
;
3316 hi
.dwContextId
= MenuInfo
.dwContextHelpID
;
3317 hi
.MousePos
= Msg
.pt
;
3318 SendMessageW(Wnd
, WM_HELP
, 0, (LPARAM
) &hi
);
3325 break; /* WM_KEYDOWN */
3334 break; /* WM_SYSKEYDOWN */
3340 if (! MenuGetRosMenuInfo(&MenuInfo
, Mt
.CurrentMenu
))
3344 if (L
'\r' == Msg
.wParam
|| L
' ' == Msg
.wParam
)
3346 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3347 fEndMenu
= (ExecutedMenuId
!= -1);
3351 /* Hack to avoid control chars. */
3352 /* We will find a better way real soon... */
3353 if (Msg
.wParam
< 32)
3358 Pos
= MenuFindItemByKey(Mt
.OwnerWnd
, &MenuInfo
,
3359 LOWORD(Msg
.wParam
), FALSE
);
3360 if ((UINT
) -2 == Pos
)
3364 else if ((UINT
) -1 == Pos
)
3370 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, Pos
, TRUE
, 0);
3371 ExecutedMenuId
= MenuExecFocusedItem(&Mt
, &MenuInfo
, Flags
);
3372 fEndMenu
= (-1 != ExecutedMenuId
);
3376 } /* switch(msg.message) - kbd */
3380 DispatchMessageW(&Msg
);
3388 /* finally remove message from the queue */
3390 if (fRemove
&& 0 == (Mt
.TrackFlags
& TF_SKIPREMOVE
))
3392 PeekMessageW(&Msg
, 0, Msg
.message
, Msg
.message
, PM_REMOVE
);
3396 Mt
.TrackFlags
&= ~TF_SKIPREMOVE
;
3400 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER
, NULL
);
3401 SetCapture(NULL
); /* release the capture */
3403 /* If dropdown is still painted and the close box is clicked on
3404 then the menu will be destroyed as part of the DispatchMessage above.
3405 This will then invalidate the menu handle in Mt.hTopMenu. We should
3406 check for this first. */
3407 if (IsMenu(Mt
.TopMenu
))
3409 if (IsWindow(Mt
.OwnerWnd
))
3411 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3413 MenuHideSubPopups(Mt
.OwnerWnd
, &MenuInfo
, FALSE
);
3415 if (0 != (MenuInfo
.Flags
& MF_POPUP
))
3417 DestroyWindow(MenuInfo
.Wnd
);
3418 MenuInfo
.Wnd
= NULL
;
3420 MenuSelectItem(Mt
.OwnerWnd
, &MenuInfo
, NO_SELECTED_ITEM
, FALSE
, NULL
);
3423 SendMessageW(Mt
.OwnerWnd
, WM_MENUSELECT
, MAKELONG(0, 0xffff), 0);
3426 if (MenuGetRosMenuInfo(&MenuInfo
, Mt
.TopMenu
))
3428 /* Reset the variable for hiding menu */
3429 MenuInfo
.TimeToHide
= FALSE
;
3430 MenuSetRosMenuInfo(&MenuInfo
);
3434 /* The return value is only used by TrackPopupMenu */
3435 return (-1 != ExecutedMenuId
) ? ExecutedMenuId
: 0;
3438 /***********************************************************************
3441 static BOOL FASTCALL
3442 MenuExitTracking(HWND Wnd
)
3444 DPRINT("hwnd=%p\n", Wnd
);
3446 SendMessageW(Wnd
, WM_EXITMENULOOP
, 0, 0);
3453 MenuTrackMouseMenuBar(HWND Wnd
, ULONG Ht
, POINT Pt
)
3455 HMENU Menu
= (HTSYSMENU
== Ht
) ? NtUserGetSystemMenu(Wnd
, FALSE
) : GetMenu(Wnd
);
3456 UINT Flags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3458 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd
, Ht
, Pt
.x
, Pt
.y
);
3462 /* map point to parent client coordinates */
3463 HWND Parent
= GetAncestor(Wnd
, GA_PARENT
);
3464 if (Parent
!= GetDesktopWindow())
3466 ScreenToClient(Parent
, &Pt
);
3469 MenuInitTracking(Wnd
, Menu
, FALSE
, Flags
);
3470 MenuTrackMenu(Menu
, Flags
, Pt
.x
, Pt
.y
, Wnd
, NULL
);
3471 MenuExitTracking(Wnd
);
3477 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
3481 /* FUNCTIONS *****************************************************************/
3484 MenuIsStringItem(ULONG TypeData)
3486 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3494 AppendMenuA(HMENU hMenu
,
3496 UINT_PTR uIDNewItem
,
3499 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3508 AppendMenuW(HMENU hMenu
,
3510 UINT_PTR uIDNewItem
,
3513 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
3522 CheckMenuItem(HMENU hmenu
,
3526 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
3534 CheckMenuRadioItem(HMENU hmenu
,
3552 return NtUserCreateMenu(FALSE
);
3560 CreatePopupMenu(VOID
)
3563 return NtUserCreateMenu(TRUE
);
3571 DeleteMenu(HMENU hMenu
,
3575 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
3583 DestroyMenu(HMENU hMenu
)
3585 return NtUserDestroyMenu(hMenu
);
3593 DrawMenuBar(HWND hWnd
)
3596 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
3605 EnableMenuItem(HMENU hMenu
,
3609 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
3619 guii
.cbSize
= sizeof(GUITHREADINFO
);
3620 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii
) && guii
.hwndMenuOwner
)
3622 PostMessageW(guii
.hwndMenuOwner
, WM_CANCELMODE
, 0, 0);
3634 return NtUserGetMenu(hWnd
);
3642 GetMenuBarInfo(HWND hwnd
,
3647 return (BOOL
)NtUserGetMenuBarInfo(hwnd
, idObject
, idItem
, pmbi
);
3655 GetMenuCheckMarkDimensions(VOID
)
3657 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
3658 GetSystemMetrics(SM_CYMENUCHECK
)));
3666 GetMenuDefaultItem(HMENU hMenu
,
3670 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
3678 GetMenuInfo(HMENU hmenu
,
3684 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
3687 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
3688 mi
.cbSize
= sizeof(MENUINFO
);
3689 mi
.fMask
= lpcmi
->fMask
;
3691 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
3693 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
3702 GetMenuItemCount(HMENU Menu
)
3704 ROSMENUINFO MenuInfo
;
3706 return MenuGetRosMenuInfo(&MenuInfo
, Menu
) ? MenuInfo
.MenuItemCount
: 0;
3714 GetMenuItemID(HMENU hMenu
,
3717 ROSMENUITEMINFO mii
;
3719 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3720 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
3722 if (! NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
3727 if (NULL
!= mii
.hSubMenu
)
3748 LPMENUITEMINFOA mii
)
3750 UNICODE_STRING Text
;
3753 if ((mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)) == 0)
3755 /* No text requested, just pass on */
3756 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3760 Text
.MaximumLength
= mii
->cch
* sizeof(WCHAR
);
3761 Text
.Buffer
= RtlAllocateHeap(GetProcessHeap(), 0, mii
->cch
* sizeof(WCHAR
));
3762 if (mii
->dwTypeData
== NULL
)
3764 AnsiString
= mii
->dwTypeData
;
3765 mii
->dwTypeData
= (LPSTR
) &Text
;
3767 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)mii
, FALSE
))
3769 HeapFree(GetProcessHeap(), 0, Text
.Buffer
);
3770 mii
->dwTypeData
= AnsiString
;
3774 if (IS_STRING_ITEM(mii
->fType
))
3776 WideCharToMultiByte(CP_ACP
, 0, Text
.Buffer
, mii
->cch
, AnsiString
, mii
->cch
,
3778 if (Text
.MaximumLength
> Text
.Length
)
3779 AnsiString
[mii
->cch
] = 0;
3782 RtlFreeHeap(GetProcessHeap(), 0, Text
.Buffer
);
3783 mii
->dwTypeData
= AnsiString
;
3797 LPMENUITEMINFOW mii
)
3799 UNICODE_STRING Text
;
3800 WCHAR
*UnicodeString
;
3802 if ((mii
->fMask
& (MIIM_STRING
| MIIM_TYPE
)) == 0)
3804 /* No text requested, just pass on */
3805 return NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
) mii
, FALSE
);
3809 Text
.MaximumLength
= mii
->cch
* sizeof(WCHAR
);
3810 Text
.Buffer
= mii
->dwTypeData
;
3811 UnicodeString
= mii
->dwTypeData
;
3812 mii
->dwTypeData
= (LPWSTR
) &Text
;
3814 if (!NtUserMenuItemInfo(Menu
, Item
, ByPosition
, (PROSMENUITEMINFO
)mii
, FALSE
))
3816 mii
->dwTypeData
= UnicodeString
;
3820 mii
->dwTypeData
= UnicodeString
;
3829 GetMenuItemRect(HWND hWnd
,
3849 ROSMENUINFO MenuInfo
;
3850 ROSMENUITEMINFO mii
;
3852 mii
.cbSize
= sizeof(MENUITEMINFOW
);
3853 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
3856 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
3861 if (! MenuGetRosMenuInfo(&MenuInfo
, mii
.hSubMenu
))
3865 nSubItems
= MenuInfo
.MenuItemCount
;
3867 /* FIXME - ported from wine, does that work (0xff)? */
3868 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
3869 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
3871 return (UINT
)-1; /* Invalid submenu */
3874 /* FIXME - ported from wine, does that work? */
3875 return (mii
.fType
| mii
.fState
);
3927 mi
.cbSize
= sizeof(MENUITEMINFOW
);
3928 mi
.fMask
= MIIM_SUBMENU
;
3930 if (NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
3932 return IsMenu(mi
.hSubMenu
) ? mi
.hSubMenu
: NULL
;
3949 TopMenu
= NtUserGetSystemMenu(hWnd
, bRevert
);
3951 return NULL
== TopMenu
? NULL
: GetSubMenu(TopMenu
, 0);
3966 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
3979 UINT_PTR uIDNewItem
,
3983 mii
.cbSize
= sizeof(MENUITEMINFOA
);
3984 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
3986 mii
.fState
= MFS_ENABLED
;
3988 if(uFlags
& MF_BITMAP
)
3990 mii
.fType
|= MFT_BITMAP
;
3991 mii
.fMask
|= MIIM_BITMAP
;
3992 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
3994 else if(uFlags
& MF_OWNERDRAW
)
3996 mii
.fType
|= MFT_OWNERDRAW
;
3997 mii
.fMask
|= MIIM_DATA
;
3998 mii
.dwItemData
= (DWORD
) lpNewItem
;
4002 mii
.fMask
|= MIIM_TYPE
;
4003 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
4004 mii
.cch
= (NULL
== lpNewItem
? 0 : strlen(lpNewItem
));
4007 if(uFlags
& MF_RIGHTJUSTIFY
)
4009 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4011 if(uFlags
& MF_MENUBREAK
)
4013 mii
.fType
|= MFT_MENUBREAK
;
4015 if(uFlags
& MF_MENUBARBREAK
)
4017 mii
.fType
|= MFT_MENUBARBREAK
;
4019 if(uFlags
& MF_DISABLED
)
4021 mii
.fState
|= MFS_DISABLED
;
4023 if(uFlags
& MF_GRAYED
)
4025 mii
.fState
|= MFS_GRAYED
;
4028 if(uFlags
& MF_POPUP
)
4030 mii
.fType
|= MF_POPUP
;
4031 mii
.fMask
|= MIIM_SUBMENU
;
4032 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4036 mii
.fMask
|= MIIM_ID
;
4037 mii
.wID
= (UINT
)uIDNewItem
;
4039 return InsertMenuItemA(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4052 LPCMENUITEMINFOA lpmii
)
4055 UNICODE_STRING MenuText
;
4057 BOOL CleanHeap
= FALSE
;
4060 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
4061 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
4063 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
4065 /* copy the text string */
4066 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4067 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4069 Status
= HEAP_strdupAtoW ( &mi
.dwTypeData
, (LPCSTR
)mi
.dwTypeData
, &mi
.cch
);
4070 if (!NT_SUCCESS (Status
))
4072 SetLastError (RtlNtStatusToDosError(Status
));
4075 RtlInitUnicodeString(&MenuText
, (PWSTR
)mi
.dwTypeData
);
4076 mi
.dwTypeData
= (LPWSTR
)&MenuText
;
4080 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4082 if ( CleanHeap
) HEAP_free ( MenuText
.Buffer
);
4097 LPCMENUITEMINFOW lpmii
)
4100 UNICODE_STRING MenuText
;
4102 mi
.hbmpItem
= (HBITMAP
)0;
4104 // while we could just pass 'lpmii' to win32k, we make a copy so that
4105 // if a bad user passes bad data, we crash his process instead of the
4108 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
4109 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
4111 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
4113 /* copy the text string */
4114 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4115 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
4119 RtlInitUnicodeString(&MenuText
, (PWSTR
)lpmii
->dwTypeData
);
4120 mi
.dwTypeData
= (LPWSTR
)&MenuText
;
4121 mi
.cch
= MenuText
.Length
/ sizeof(WCHAR
);
4125 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
4140 UINT_PTR uIDNewItem
,
4144 mii
.cbSize
= sizeof(MENUITEMINFOW
);
4145 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
| MIIM_STATE
;
4147 mii
.fState
= MFS_ENABLED
;
4149 if(uFlags
& MF_BITMAP
)
4151 mii
.fType
|= MFT_BITMAP
;
4152 mii
.fMask
|= MIIM_BITMAP
;
4153 mii
.hbmpItem
= (HBITMAP
) lpNewItem
;
4155 else if(uFlags
& MF_OWNERDRAW
)
4157 mii
.fType
|= MFT_OWNERDRAW
;
4158 mii
.fMask
|= MIIM_DATA
;
4159 mii
.dwItemData
= (DWORD
) lpNewItem
;
4163 mii
.fMask
|= MIIM_TYPE
;
4164 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
4165 mii
.cch
= (NULL
== lpNewItem
? 0 : wcslen(lpNewItem
));
4168 if(uFlags
& MF_RIGHTJUSTIFY
)
4170 mii
.fType
|= MFT_RIGHTJUSTIFY
;
4172 if(uFlags
& MF_MENUBREAK
)
4174 mii
.fType
|= MFT_MENUBREAK
;
4176 if(uFlags
& MF_MENUBARBREAK
)
4178 mii
.fType
|= MFT_MENUBARBREAK
;
4180 if(uFlags
& MF_DISABLED
)
4182 mii
.fState
|= MFS_DISABLED
;
4184 if(uFlags
& MF_GRAYED
)
4186 mii
.fState
|= MFS_GRAYED
;
4189 if(uFlags
& MF_POPUP
)
4191 mii
.fType
|= MF_POPUP
;
4192 mii
.fMask
|= MIIM_SUBMENU
;
4193 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
4197 mii
.fMask
|= MIIM_ID
;
4198 mii
.wID
= (UINT
)uIDNewItem
;
4200 return InsertMenuItemW(hMenu
, uPosition
, (BOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
4212 ROSMENUINFO MenuInfo
;
4214 return MenuGetRosMenuInfo(&MenuInfo
, Menu
);
4222 LoadMenuA(HINSTANCE hInstance
,
4225 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
4226 if (Resource
== NULL
)
4230 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
4238 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
4240 return(LoadMenuIndirectW(lpMenuTemplate
));
4248 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
4251 WORD version
, offset
;
4252 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
4254 version
= GET_WORD(p
);
4259 case 0: /* standard format is version of 0 */
4260 offset
= GET_WORD(p
);
4261 p
+= sizeof(WORD
) + offset
;
4262 if (!(hMenu
= CreateMenu())) return 0;
4263 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
4269 case 1: /* extended format is version of 1 */
4270 offset
= GET_WORD(p
);
4271 p
+= sizeof(WORD
) + offset
;
4272 if (!(hMenu
= CreateMenu())) return 0;
4273 if (!MENUEX_ParseResource(p
, hMenu
))
4275 DestroyMenu( hMenu
);
4280 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
4290 LoadMenuW(HINSTANCE hInstance
,
4293 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
4294 if (Resource
== NULL
)
4298 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
4312 return NtUserMenuItemFromPoint(hWnd
, hMenu
, ptScreen
.x
, ptScreen
.y
);
4325 UINT_PTR uIDNewItem
,
4342 UINT_PTR uIDNewItem
,
4360 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
4371 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
4385 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
4400 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
4403 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
4404 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
4417 HBITMAP hBitmapUnchecked
,
4418 HBITMAP hBitmapChecked
)
4434 LPCMENUITEMINFOA lpmii
)
4436 MENUITEMINFOW MenuItemInfoW
;
4437 UNICODE_STRING UnicodeString
;
4440 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4442 if ((MenuItemInfoW
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4443 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
) &&
4444 MenuItemInfoW
.dwTypeData
)
4446 RtlCreateUnicodeStringFromAsciiz(&UnicodeString
,
4447 (LPSTR
)MenuItemInfoW
.dwTypeData
);
4448 MenuItemInfoW
.dwTypeData
= (LPWSTR
)&UnicodeString
;
4452 UnicodeString
.Buffer
= NULL
;
4455 Result
= NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4456 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4458 if (UnicodeString
.Buffer
!= NULL
)
4460 RtlFreeUnicodeString(&UnicodeString
);
4476 LPCMENUITEMINFOW lpmii
)
4478 MENUITEMINFOW MenuItemInfoW
;
4479 UNICODE_STRING UnicodeString
;
4481 RtlCopyMemory(&MenuItemInfoW
, lpmii
, min(lpmii
->cbSize
, sizeof(MENUITEMINFOW
)));
4483 if ((MenuItemInfoW
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
4484 (MENU_ITEM_TYPE(MenuItemInfoW
.fType
) == MF_STRING
) &&
4485 MenuItemInfoW
.dwTypeData
)
4487 RtlInitUnicodeString(&UnicodeString
, MenuItemInfoW
.dwTypeData
);
4488 MenuItemInfoW
.dwTypeData
= (LPWSTR
)&UnicodeString
;
4491 return NtUserMenuItemInfo(hMenu
, uItem
, fByPosition
,
4492 (PROSMENUITEMINFO
)&MenuItemInfoW
, TRUE
);
4506 SetLastError(ERROR_INVALID_WINDOW_HANDLE
);
4511 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4514 return NtUserSetSystemMenu(hwnd
, hMenu
);
4534 MenuInitTracking(Wnd
, Menu
, TRUE
, Flags
);
4536 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4537 if (0 == (Flags
& TPM_NONOTIFY
))
4539 SendMessageW(Wnd
, WM_INITMENUPOPUP
, (WPARAM
) Menu
, 0);
4542 if (MenuShowPopup(Wnd
, Menu
, 0, x
, y
, 0, 0 ))
4544 ret
= MenuTrackMenu(Menu
, Flags
| TPM_POPUPMENU
, 0, 0, Wnd
, Rect
);
4546 MenuExitTracking(Wnd
);
4565 /* Not fully implemented */
4566 return TrackPopupMenu(Menu
, Flags
, x
, y
, 0, Wnd
,
4567 NULL
!= Tpm
? &Tpm
->rcExclude
: NULL
);
4576 SetMenuContextHelpId(HMENU hmenu
,
4577 DWORD dwContextHelpId
)
4579 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
4588 GetMenuContextHelpId(HMENU hmenu
)
4591 mi
.cbSize
= sizeof(ROSMENUINFO
);
4592 mi
.fMask
= MIM_HELPID
;
4594 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
4596 return mi
.dwContextHelpID
;
4641 LPCWSTR lpszNewItem
,
4646 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4647 for MF_DELETE. We should check the parameters for all others
4648 MF_* actions also (anybody got a doc on ChangeMenu?).
4651 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4654 return AppendMenuW(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4657 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4660 return ModifyMenuW(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4663 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4664 flags
&~ MF_REMOVE
);
4666 default : /* MF_INSERT */
4667 return InsertMenuW(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);
4684 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4685 for MF_DELETE. We should check the parameters for all others
4686 MF_* actions also (anybody got a doc on ChangeMenu?).
4689 switch(flags
& (MF_APPEND
| MF_DELETE
| MF_CHANGE
| MF_REMOVE
| MF_INSERT
))
4692 return AppendMenuA(hMenu
, flags
&~ MF_APPEND
, cmdInsert
, lpszNewItem
);
4695 return DeleteMenu(hMenu
, cmd
, flags
&~ MF_DELETE
);
4698 return ModifyMenuA(hMenu
, cmd
, flags
&~ MF_CHANGE
, cmdInsert
, lpszNewItem
);
4701 return RemoveMenu(hMenu
, flags
& MF_BYPOSITION
? cmd
: cmdInsert
,
4702 flags
&~ MF_REMOVE
);
4704 default : /* MF_INSERT */
4705 return InsertMenuA(hMenu
, cmd
, flags
, cmdInsert
, lpszNewItem
);