2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
5 * FILE: win32ss/user/ntuser/menu.c
6 * PROGRAMER: Thomas Weidenmueller (w3seek@users.sourceforge.net)
10 DBG_DEFAULT_CHANNEL(UserMenu
);
12 /* INTERNAL ******************************************************************/
14 HFONT ghMenuFont
= NULL
;
15 HFONT ghMenuFontBold
= NULL
;
16 static SIZE MenuCharSize
;
18 /* Use global popup window because there's no way 2 menus can
19 * be tracked at the same time. */
20 static HWND top_popup
= NULL
;
21 static HMENU top_popup_hmenu
= NULL
;
23 BOOL fInsideMenuLoop
= FALSE
;
24 BOOL fInEndMenu
= FALSE
;
26 /* internal popup menu window messages */
28 #define MM_SETMENUHANDLE (WM_USER + 0)
29 #define MM_GETMENUHANDLE (WM_USER + 1)
31 /* internal flags for menu tracking */
33 #define TF_ENDMENU 0x10000
34 #define TF_SUSPENDPOPUP 0x20000
35 #define TF_SKIPREMOVE 0x40000
38 /* maximum allowed depth of any branch in the menu tree.
39 * This value is slightly larger than in windows (25) to
40 * stay on the safe side. */
41 #define MAXMENUDEPTH 30
43 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
45 #define MENUITEMINFO_TYPE_MASK \
46 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
47 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
48 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
50 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
52 #define STATE_MASK (~TYPE_MASK)
54 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
56 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
58 #define IS_SYSTEM_MENU(MenuInfo) \
59 (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU))
61 #define IS_SYSTEM_POPUP(MenuInfo) \
62 (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU))
64 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
66 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
67 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
69 /* Maximum number of menu items a menu can contain */
70 #define MAX_MENU_ITEMS (0x4000)
71 #define MAX_GOINTOSUBMENU (0x10)
73 /* Space between 2 columns */
74 #define MENU_COL_SPACE 4
76 /* top and bottom margins for popup menus */
77 #define MENU_TOP_MARGIN 2 //3
78 #define MENU_BOTTOM_MARGIN 2
80 #define MENU_ITEM_HBMP_SPACE (5)
81 #define MENU_BAR_ITEMS_SPACE (12)
82 #define SEPARATOR_HEIGHT (5)
83 #define MENU_TAB_SPACE (8)
88 PMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
89 PMENU TopMenu
; /* initial menu */
90 PWND OwnerWnd
; /* where notifications are sent */
94 /* Internal MenuTrackMenu() flags */
95 #define TPM_INTERNAL 0xF0000000
96 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
97 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
102 #define UpdateMenuItemState(state, change) \
104 if((change) & MF_GRAYED) { \
105 (state) |= MF_GRAYED; \
107 (state) &= ~MF_GRAYED; \
108 } /* Separate the two for test_menu_resource_layout.*/ \
109 if((change) & MF_DISABLED) { \
110 (state) |= MF_DISABLED; \
112 (state) &= ~MF_DISABLED; \
114 if((change) & MFS_CHECKED) { \
115 (state) |= MFS_CHECKED; \
117 (state) &= ~MFS_CHECKED; \
119 if((change) & MFS_HILITE) { \
120 (state) |= MFS_HILITE; \
122 (state) &= ~MFS_HILITE; \
124 if((change) & MFS_DEFAULT) { \
125 (state) |= MFS_DEFAULT; \
127 (state) &= ~MFS_DEFAULT; \
129 if((change) & MF_MOUSESELECT) { \
130 (state) |= MF_MOUSESELECT; \
132 (state) &= ~MF_MOUSESELECT; \
138 DumpMenuItemList(PMENU Menu
, PITEM MenuItem
)
140 UINT cnt
= 0, i
= Menu
->cItems
;
143 if(MenuItem
->lpstr
.Length
)
144 DbgPrint(" %d. %wZ\n", ++cnt
, &MenuItem
->lpstr
);
146 DbgPrint(" %d. NO TEXT dwTypeData==%d\n", ++cnt
, (DWORD
)MenuItem
->lpstr
.Buffer
);
148 if(MFT_BITMAP
& MenuItem
->fType
)
149 DbgPrint("MFT_BITMAP ");
150 if(MFT_MENUBARBREAK
& MenuItem
->fType
)
151 DbgPrint("MFT_MENUBARBREAK ");
152 if(MFT_MENUBREAK
& MenuItem
->fType
)
153 DbgPrint("MFT_MENUBREAK ");
154 if(MFT_OWNERDRAW
& MenuItem
->fType
)
155 DbgPrint("MFT_OWNERDRAW ");
156 if(MFT_RADIOCHECK
& MenuItem
->fType
)
157 DbgPrint("MFT_RADIOCHECK ");
158 if(MFT_RIGHTJUSTIFY
& MenuItem
->fType
)
159 DbgPrint("MFT_RIGHTJUSTIFY ");
160 if(MFT_SEPARATOR
& MenuItem
->fType
)
161 DbgPrint("MFT_SEPARATOR ");
162 if(MFT_STRING
& MenuItem
->fType
)
163 DbgPrint("MFT_STRING ");
164 DbgPrint("\n fState=");
165 if(MFS_DISABLED
& MenuItem
->fState
)
166 DbgPrint("MFS_DISABLED ");
168 DbgPrint("MFS_ENABLED ");
169 if(MFS_CHECKED
& MenuItem
->fState
)
170 DbgPrint("MFS_CHECKED ");
172 DbgPrint("MFS_UNCHECKED ");
173 if(MFS_HILITE
& MenuItem
->fState
)
174 DbgPrint("MFS_HILITE ");
176 DbgPrint("MFS_UNHILITE ");
177 if(MFS_DEFAULT
& MenuItem
->fState
)
178 DbgPrint("MFS_DEFAULT ");
179 if(MFS_GRAYED
& MenuItem
->fState
)
180 DbgPrint("MFS_GRAYED ");
181 DbgPrint("\n wId=%d\n", MenuItem
->wID
);
185 DbgPrint("Entries: %d\n", cnt
);
190 #define FreeMenuText(Menu,MenuItem) \
192 if((MENU_ITEM_TYPE((MenuItem)->fType) == MF_STRING) && \
193 (MenuItem)->lpstr.Length) { \
194 DesktopHeapFree(((PMENU)Menu)->head.rpdesk, (MenuItem)->lpstr.Buffer); \
199 IntGetMenuObject(HMENU hMenu
)
201 PMENU Menu
= UserGetMenuObject(hMenu
);
203 Menu
->head
.cLockObj
++;
208 PMENU FASTCALL
VerifyMenu(PMENU pMenu
)
214 if (!pMenu
) return NULL
;
216 Error
= EngGetLastError();
220 hMenu
= UserHMGetHandle(pMenu
);
221 pItem
= pMenu
->rgItems
;
228 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
230 ERR("Run away LOOP!\n");
231 EngSetLastError(Error
);
232 _SEH2_YIELD(return NULL
);
236 if ( UserObjectInDestroy(hMenu
))
238 ERR("Menu is marked for destruction!\n");
241 EngSetLastError(Error
);
247 IntIsMenu(HMENU Menu
)
249 if (UserGetMenuObject(Menu
)) return TRUE
;
255 IntGetMenu(HWND hWnd
)
257 PWND Wnd
= ValidateHwndNoErr(hWnd
);
262 return UserGetMenuObject(UlongToHandle(Wnd
->IDMenu
));
265 PMENU
get_win_sys_menu( HWND hwnd
)
268 WND
*win
= ValidateHwndNoErr( hwnd
);
271 ret
= UserGetMenuObject(win
->SystemMenu
);
276 BOOL
IntDestroyMenu( PMENU pMenu
, BOOL bRecurse
)
280 if (pMenu
->rgItems
) /* recursively destroy submenus */
283 ITEM
*item
= pMenu
->rgItems
;
284 for (i
= pMenu
->cItems
; i
> 0; i
--, item
++)
286 SubMenu
= item
->spSubMenu
;
287 item
->spSubMenu
= NULL
;
289 /* Remove Item Text */
290 FreeMenuText(pMenu
,item
);
292 /* Remove Item Bitmap and set it for this process */
293 if (item
->hbmp
&& !(item
->fState
& MFS_HBMMENUBMP
))
295 GreSetObjectOwner(item
->hbmp
, GDI_OBJ_HMGR_POWNED
);
299 /* Remove Item submenu */
300 if (bRecurse
&& SubMenu
)//VerifyMenu(SubMenu))
302 /* Release submenu since it was referenced when inserted */
303 IntReleaseMenuObject(SubMenu
);
304 IntDestroyMenuObject(SubMenu
, bRecurse
);
308 DesktopHeapFree(pMenu
->head
.rpdesk
, pMenu
->rgItems
);
309 pMenu
->rgItems
= NULL
;
315 /* Callback for the object manager */
317 UserDestroyMenuObject(PVOID Object
)
319 return IntDestroyMenuObject(Object
, TRUE
);
323 IntDestroyMenuObject(PMENU Menu
, BOOL bRecurse
)
329 if (PsGetCurrentProcessSessionId() == Menu
->head
.rpdesk
->rpwinstaParent
->dwSessionId
)
334 Window
= ValidateHwndNoErr(Menu
->hWnd
);
337 //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
339 /* DestroyMenu should not destroy system menu popup owner */
340 if ((Menu
->fFlags
& (MNF_POPUP
| MNF_SYSSUBMENU
)) == MNF_POPUP
)
342 // Should we check it to see if it has Class?
343 ERR("FIXME Pop up menu window thing'ie\n");
344 //co_UserDestroyWindow( Window );
350 if (!UserMarkObjectDestroy(Menu
)) return TRUE
;
352 /* Remove all menu items */
353 IntDestroyMenu( Menu
, bRecurse
);
355 ret
= UserDeleteObject(Menu
->head
.h
, TYPE_MENU
);
356 TRACE("IntDestroyMenuObject %d\n",ret
);
366 NONCLIENTMETRICSW ncm
;
368 /* get the menu font */
369 if (!ghMenuFont
|| !ghMenuFontBold
)
371 ncm
.cbSize
= sizeof(ncm
);
372 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
374 ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
378 ghMenuFont
= GreCreateFontIndirectW(&ncm
.lfMenuFont
);
379 if (ghMenuFont
== NULL
)
381 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
384 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
385 ghMenuFontBold
= GreCreateFontIndirectW(&ncm
.lfMenuFont
);
386 if (ghMenuFontBold
== NULL
)
388 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
389 GreDeleteObject(ghMenuFont
);
394 GreSetObjectOwner(ghMenuFont
, GDI_OBJ_HMGR_PUBLIC
);
395 GreSetObjectOwner(ghMenuFontBold
, GDI_OBJ_HMGR_PUBLIC
);
404 /**********************************************************************
407 * detect if there are loops in the menu tree (or the depth is too large)
409 int FASTCALL
MENU_depth( PMENU pmenu
, int depth
)
415 if (!pmenu
) return depth
;
418 if( depth
> MAXMENUDEPTH
) return depth
;
419 item
= pmenu
->rgItems
;
421 for( i
= 0; i
< pmenu
->cItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++)
423 if( item
->spSubMenu
)//VerifyMenu(item->spSubMenu))
425 int bdepth
= MENU_depth( item
->spSubMenu
, depth
);
426 if( bdepth
> subdepth
) subdepth
= bdepth
;
428 if( subdepth
> MAXMENUDEPTH
)
429 TRACE("<- hmenu %p\n", item
->spSubMenu
);
435 /******************************************************************************
437 * UINT MenuGetStartOfNextColumn(
440 *****************************************************************************/
442 static UINT
MENU_GetStartOfNextColumn(
449 return NO_SELECTED_ITEM
;
452 if( i
== NO_SELECTED_ITEM
)
455 pItem
= menu
->rgItems
;
456 if (!pItem
) return NO_SELECTED_ITEM
;
458 for( ; i
< menu
->cItems
; ++i
) {
459 if (pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
463 return NO_SELECTED_ITEM
;
466 /******************************************************************************
468 * UINT MenuGetStartOfPrevColumn(
471 *****************************************************************************/
472 static UINT
MENU_GetStartOfPrevColumn(
479 return NO_SELECTED_ITEM
;
481 if( menu
->iItem
== 0 || menu
->iItem
== NO_SELECTED_ITEM
)
482 return NO_SELECTED_ITEM
;
484 pItem
= menu
->rgItems
;
485 if (!pItem
) return NO_SELECTED_ITEM
;
487 /* Find the start of the column */
489 for(i
= menu
->iItem
; i
!= 0 &&
490 !(pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
494 return NO_SELECTED_ITEM
;
496 for(--i
; i
!= 0; --i
) {
497 if (pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
501 TRACE("ret %d.\n", i
);
506 /***********************************************************************
509 * Find a menu item. Return a pointer on the item, and modifies *hmenu
510 * in case the item was in a sub-menu.
512 PITEM FASTCALL
MENU_FindItem( PMENU
*pmenu
, UINT
*nPos
, UINT wFlags
)
515 ITEM
*fallback
= NULL
;
516 UINT fallback_pos
= 0;
519 if (!menu
) return NULL
;
521 if (wFlags
& MF_BYPOSITION
)
523 if (!menu
->cItems
) return NULL
;
524 if (*nPos
>= menu
->cItems
) return NULL
;
525 return &menu
->rgItems
[*nPos
];
529 PITEM item
= menu
->rgItems
;
530 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
534 PMENU psubmenu
= item
->spSubMenu
;//VerifyMenu(item->spSubMenu);
535 PITEM subitem
= MENU_FindItem( &psubmenu
, nPos
, wFlags
);
541 else if (item
->wID
== *nPos
)
543 /* fallback to this item if nothing else found */
548 else if (item
->wID
== *nPos
)
557 *nPos
= fallback_pos
;
562 /***********************************************************************
565 * Find a Sub menu. Return the position of the submenu, and modifies
566 * *hmenu in case it is found in another sub-menu.
567 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
569 static UINT FASTCALL
MENU_FindSubMenu(PMENU
*menu
, PMENU SubTarget
)
574 item
= ((PMENU
)*menu
)->rgItems
;
575 for (i
= 0; i
< ((PMENU
)*menu
)->cItems
; i
++, item
++)
577 if (!item
->spSubMenu
)
581 if (item
->spSubMenu
== SubTarget
)
587 PMENU pSubMenu
= item
->spSubMenu
;
588 UINT pos
= MENU_FindSubMenu( &pSubMenu
, SubTarget
);
589 if (pos
!= NO_SELECTED_ITEM
)
597 return NO_SELECTED_ITEM
;
601 IntRemoveMenuItem( PMENU pMenu
, UINT nPos
, UINT wFlags
, BOOL bRecurse
)
605 TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu
, nPos
, wFlags
);
606 if (!(item
= MENU_FindItem( &pMenu
, &nPos
, wFlags
))) return FALSE
;
610 FreeMenuText(pMenu
,item
);
611 if (bRecurse
&& item
->spSubMenu
)
613 IntDestroyMenuObject(item
->spSubMenu
, bRecurse
);
615 ////// Use cAlloced with inc's of 8's....
616 if (--pMenu
->cItems
== 0)
618 DesktopHeapFree(pMenu
->head
.rpdesk
, pMenu
->rgItems
);
619 pMenu
->rgItems
= NULL
;
623 while(nPos
< pMenu
->cItems
)
629 pMenu
->rgItems
= DesktopHeapReAlloc(pMenu
->head
.rpdesk
, pMenu
->rgItems
, pMenu
->cItems
* sizeof(ITEM
));
634 /**********************************************************************
637 * Insert (allocate) a new item into a menu.
639 ITEM
*MENU_InsertItem( PMENU menu
, UINT pos
, UINT flags
, PMENU
*submenu
, UINT
*npos
)
643 /* Find where to insert new item */
645 if (flags
& MF_BYPOSITION
) {
646 if (pos
> menu
->cItems
)
649 if (!MENU_FindItem( &menu
, &pos
, flags
))
651 if (submenu
) *submenu
= menu
;
652 if (npos
) *npos
= pos
;
657 /* Make sure that MDI system buttons stay on the right side.
658 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
659 * regardless of their id.
662 (INT_PTR
)menu
->rgItems
[pos
- 1].hbmp
>= (INT_PTR
)HBMMENU_SYSTEM
&&
663 (INT_PTR
)menu
->rgItems
[pos
- 1].hbmp
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
666 TRACE("inserting at %u flags %x\n", pos
, flags
);
668 /* Create new items array */
670 newItems
= DesktopHeapAlloc(menu
->head
.rpdesk
, sizeof(ITEM
) * (menu
->cItems
+1) );
673 WARN("allocation failed\n" );
676 if (menu
->cItems
> 0)
678 /* Copy the old array into the new one */
679 if (pos
> 0) RtlCopyMemory( newItems
, menu
->rgItems
, pos
* sizeof(ITEM
) );
680 if (pos
< menu
->cItems
) RtlCopyMemory( &newItems
[pos
+1], &menu
->rgItems
[pos
], (menu
->cItems
-pos
)*sizeof(ITEM
) );
681 DesktopHeapFree(menu
->head
.rpdesk
, menu
->rgItems
);
683 menu
->rgItems
= newItems
;
685 RtlZeroMemory( &newItems
[pos
], sizeof(*newItems
) );
686 menu
->cyMenu
= 0; /* force size recalculate */
687 return &newItems
[pos
];
692 _In_ PMENU MenuObject
,
695 PROSMENUITEMINFO ItemInfo
,
696 PUNICODE_STRING lpstr
)
699 PMENU SubMenu
= NULL
;
701 NT_ASSERT(MenuObject
!= NULL
);
703 if (MAX_MENU_ITEMS
<= MenuObject
->cItems
)
705 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
709 SubMenu
= MenuObject
;
711 if(!(MenuItem
= MENU_InsertItem( SubMenu
, uItem
, fByPosition
? MF_BYPOSITION
: MF_BYCOMMAND
, &SubMenu
, &uItem
))) return FALSE
;
713 if(!IntSetMenuItemInfo(SubMenu
, MenuItem
, ItemInfo
, lpstr
))
715 IntRemoveMenuItem(SubMenu
, uItem
, fByPosition
? MF_BYPOSITION
: MF_BYCOMMAND
, FALSE
);
719 /* Force size recalculation! */
721 MenuItem
->hbmpChecked
= MenuItem
->hbmpUnchecked
= 0;
723 TRACE("IntInsertMenuItemToList = %u %i\n", uItem
, (BOOL
)((INT
)uItem
>= 0));
730 _Out_ PHANDLE Handle
,
732 _In_ PDESKTOP Desktop
,
733 _In_ PPROCESSINFO ppi
)
737 Menu
= (PMENU
)UserCreateObject( gHandleTable
,
749 Menu
->cyMax
= 0; /* Default */
750 Menu
->hbrBack
= NULL
; /* No brush */
751 Menu
->dwContextHelpId
= 0; /* Default */
752 Menu
->dwMenuData
= 0; /* Default */
753 Menu
->iItem
= NO_SELECTED_ITEM
; // Focused item
754 Menu
->fFlags
= (IsMenuBar
? 0 : MNF_POPUP
);
755 Menu
->spwndNotify
= NULL
;
756 Menu
->cyMenu
= 0; // Height
757 Menu
->cxMenu
= 0; // Width
758 Menu
->cItems
= 0; // Item count
761 Menu
->cxTextAlign
= 0;
762 Menu
->rgItems
= NULL
;
765 Menu
->TimeToHide
= FALSE
;
771 IntCloneMenuItems(PMENU Destination
, PMENU Source
)
773 PITEM MenuItem
, NewMenuItem
= NULL
;
779 NewMenuItem
= DesktopHeapAlloc(Destination
->head
.rpdesk
, (Source
->cItems
+1) * sizeof(ITEM
));
780 if(!NewMenuItem
) return FALSE
;
782 RtlZeroMemory(NewMenuItem
, (Source
->cItems
+1) * sizeof(ITEM
));
784 Destination
->rgItems
= NewMenuItem
;
786 MenuItem
= Source
->rgItems
;
787 for (i
= 0; i
< Source
->cItems
; i
++, MenuItem
++, NewMenuItem
++)
789 NewMenuItem
->fType
= MenuItem
->fType
;
790 NewMenuItem
->fState
= MenuItem
->fState
;
791 NewMenuItem
->wID
= MenuItem
->wID
;
792 NewMenuItem
->spSubMenu
= MenuItem
->spSubMenu
;
793 NewMenuItem
->hbmpChecked
= MenuItem
->hbmpChecked
;
794 NewMenuItem
->hbmpUnchecked
= MenuItem
->hbmpUnchecked
;
795 NewMenuItem
->dwItemData
= MenuItem
->dwItemData
;
796 if (MenuItem
->lpstr
.Length
)
798 NewMenuItem
->lpstr
.Length
= 0;
799 NewMenuItem
->lpstr
.MaximumLength
= MenuItem
->lpstr
.MaximumLength
;
800 NewMenuItem
->lpstr
.Buffer
= DesktopHeapAlloc(Destination
->head
.rpdesk
, MenuItem
->lpstr
.MaximumLength
);
801 if (!NewMenuItem
->lpstr
.Buffer
)
803 DesktopHeapFree(Destination
->head
.rpdesk
, NewMenuItem
);
806 RtlCopyUnicodeString(&NewMenuItem
->lpstr
, &MenuItem
->lpstr
);
807 NewMenuItem
->lpstr
.Buffer
[MenuItem
->lpstr
.Length
/ sizeof(WCHAR
)] = 0;
808 NewMenuItem
->Xlpstr
= NewMenuItem
->lpstr
.Buffer
;
812 NewMenuItem
->lpstr
.Buffer
= MenuItem
->lpstr
.Buffer
;
813 NewMenuItem
->Xlpstr
= NewMenuItem
->lpstr
.Buffer
;
815 NewMenuItem
->hbmp
= MenuItem
->hbmp
;
821 IntCloneMenu(PMENU Source
)
829 /* A menu is valid process wide. We can pass to the object manager any thread ptr */
830 Menu
= (PMENU
)UserCreateObject( gHandleTable
,
832 ((PPROCESSINFO
)Source
->head
.hTaskWow
)->ptiList
,
839 Menu
->fFlags
= Source
->fFlags
;
840 Menu
->cyMax
= Source
->cyMax
;
841 Menu
->hbrBack
= Source
->hbrBack
;
842 Menu
->dwContextHelpId
= Source
->dwContextHelpId
;
843 Menu
->dwMenuData
= Source
->dwMenuData
;
844 Menu
->iItem
= NO_SELECTED_ITEM
;
845 Menu
->spwndNotify
= NULL
;
848 Menu
->cItems
= Source
->cItems
;
851 Menu
->cxTextAlign
= 0;
852 Menu
->rgItems
= NULL
;
855 Menu
->TimeToHide
= FALSE
;
857 IntCloneMenuItems(Menu
, Source
);
863 IntSetMenuFlagRtoL(PMENU Menu
)
865 ERR("SetMenuFlagRtoL\n");
866 Menu
->fFlags
|= MNF_RTOL
;
871 IntSetMenuContextHelpId(PMENU Menu
, DWORD dwContextHelpId
)
873 Menu
->dwContextHelpId
= dwContextHelpId
;
878 IntGetMenuInfo(PMENU Menu
, PROSMENUINFO lpmi
)
880 if(lpmi
->fMask
& MIM_BACKGROUND
)
881 lpmi
->hbrBack
= Menu
->hbrBack
;
882 if(lpmi
->fMask
& MIM_HELPID
)
883 lpmi
->dwContextHelpID
= Menu
->dwContextHelpId
;
884 if(lpmi
->fMask
& MIM_MAXHEIGHT
)
885 lpmi
->cyMax
= Menu
->cyMax
;
886 if(lpmi
->fMask
& MIM_MENUDATA
)
887 lpmi
->dwMenuData
= Menu
->dwMenuData
;
888 if(lpmi
->fMask
& MIM_STYLE
)
889 lpmi
->dwStyle
= Menu
->fFlags
& MNS_STYLE_MASK
;
891 if (sizeof(MENUINFO
) < lpmi
->cbSize
)
893 lpmi
->cItems
= Menu
->cItems
;
895 lpmi
->iItem
= Menu
->iItem
;
896 lpmi
->cxMenu
= Menu
->cxMenu
;
897 lpmi
->cyMenu
= Menu
->cyMenu
;
898 lpmi
->spwndNotify
= Menu
->spwndNotify
;
899 lpmi
->cxTextAlign
= Menu
->cxTextAlign
;
900 lpmi
->iTop
= Menu
->iTop
;
901 lpmi
->iMaxTop
= Menu
->iMaxTop
;
902 lpmi
->dwArrowsOn
= Menu
->dwArrowsOn
;
904 lpmi
->fFlags
= Menu
->fFlags
;
905 lpmi
->Self
= Menu
->head
.h
;
906 lpmi
->TimeToHide
= Menu
->TimeToHide
;
907 lpmi
->Wnd
= Menu
->hWnd
;
913 IntSetMenuInfo(PMENU Menu
, PROSMENUINFO lpmi
)
915 if(lpmi
->fMask
& MIM_BACKGROUND
)
916 Menu
->hbrBack
= lpmi
->hbrBack
;
917 if(lpmi
->fMask
& MIM_HELPID
)
918 Menu
->dwContextHelpId
= lpmi
->dwContextHelpID
;
919 if(lpmi
->fMask
& MIM_MAXHEIGHT
)
920 Menu
->cyMax
= lpmi
->cyMax
;
921 if(lpmi
->fMask
& MIM_MENUDATA
)
922 Menu
->dwMenuData
= lpmi
->dwMenuData
;
923 if(lpmi
->fMask
& MIM_STYLE
)
924 Menu
->fFlags
^= (Menu
->fFlags
^ lpmi
->dwStyle
) & MNS_STYLE_MASK
;
925 if(lpmi
->fMask
& MIM_APPLYTOSUBMENUS
)
928 PITEM item
= Menu
->rgItems
;
929 for ( i
= Menu
->cItems
; i
; i
--, item
++)
931 if ( item
->spSubMenu
)
933 IntSetMenuInfo( item
->spSubMenu
, lpmi
);
937 if (sizeof(MENUINFO
) < lpmi
->cbSize
)
939 Menu
->iItem
= lpmi
->iItem
;
940 Menu
->cyMenu
= lpmi
->cyMenu
;
941 Menu
->cxMenu
= lpmi
->cxMenu
;
942 Menu
->spwndNotify
= lpmi
->spwndNotify
;
943 Menu
->cxTextAlign
= lpmi
->cxTextAlign
;
944 Menu
->iTop
= lpmi
->iTop
;
945 Menu
->iMaxTop
= lpmi
->iMaxTop
;
946 Menu
->dwArrowsOn
= lpmi
->dwArrowsOn
;
948 Menu
->TimeToHide
= lpmi
->TimeToHide
;
949 Menu
->hWnd
= lpmi
->Wnd
;
951 if ( lpmi
->fMask
& MIM_STYLE
)
953 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented wine\n");
954 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented wine\n");
955 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented wine\n");
961 IntGetMenuItemInfo(PMENU Menu
, /* UNUSED PARAM!! */
962 PITEM MenuItem
, PROSMENUITEMINFO lpmii
)
966 if(lpmii
->fMask
& (MIIM_FTYPE
| MIIM_TYPE
))
968 lpmii
->fType
= MenuItem
->fType
;
970 if(lpmii
->fMask
& MIIM_BITMAP
)
972 lpmii
->hbmpItem
= MenuItem
->hbmp
;
974 if(lpmii
->fMask
& MIIM_CHECKMARKS
)
976 lpmii
->hbmpChecked
= MenuItem
->hbmpChecked
;
977 lpmii
->hbmpUnchecked
= MenuItem
->hbmpUnchecked
;
979 if(lpmii
->fMask
& MIIM_DATA
)
981 lpmii
->dwItemData
= MenuItem
->dwItemData
;
983 if(lpmii
->fMask
& MIIM_ID
)
985 lpmii
->wID
= MenuItem
->wID
;
987 if(lpmii
->fMask
& MIIM_STATE
)
989 lpmii
->fState
= MenuItem
->fState
;
991 if(lpmii
->fMask
& MIIM_SUBMENU
)
993 lpmii
->hSubMenu
= MenuItem
->spSubMenu
? MenuItem
->spSubMenu
->head
.h
: NULL
;
996 if ((lpmii
->fMask
& MIIM_STRING
) ||
997 ((lpmii
->fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(lpmii
->fType
) == MF_STRING
)))
999 if (lpmii
->dwTypeData
== NULL
)
1001 lpmii
->cch
= MenuItem
->lpstr
.Length
/ sizeof(WCHAR
);
1004 { //// lpmii->lpstr can be read in user mode!!!!
1005 Status
= MmCopyToCaller(lpmii
->dwTypeData
, MenuItem
->lpstr
.Buffer
,
1006 min(lpmii
->cch
* sizeof(WCHAR
),
1007 MenuItem
->lpstr
.MaximumLength
));
1008 if (! NT_SUCCESS(Status
))
1010 SetLastNtError(Status
);
1016 if (sizeof(ROSMENUITEMINFO
) == lpmii
->cbSize
)
1018 lpmii
->Rect
.left
= MenuItem
->xItem
;
1019 lpmii
->Rect
.top
= MenuItem
->yItem
;
1020 lpmii
->Rect
.right
= MenuItem
->cxItem
; // Do this for now......
1021 lpmii
->Rect
.bottom
= MenuItem
->cyItem
;
1022 lpmii
->dxTab
= MenuItem
->dxTab
;
1023 lpmii
->lpstr
= MenuItem
->lpstr
.Buffer
;
1024 lpmii
->maxBmpSize
.cx
= MenuItem
->cxBmp
;
1025 lpmii
->maxBmpSize
.cy
= MenuItem
->cyBmp
;
1032 IntSetMenuItemInfo(PMENU MenuObject
, PITEM MenuItem
, PROSMENUITEMINFO lpmii
, PUNICODE_STRING lpstr
)
1034 PMENU SubMenuObject
;
1035 BOOL circref
= FALSE
;
1037 if(!MenuItem
|| !MenuObject
|| !lpmii
)
1041 if ( lpmii
->fMask
& MIIM_FTYPE
)
1043 MenuItem
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
1044 MenuItem
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
1046 if (lpmii
->fMask
& MIIM_TYPE
)
1048 #if 0 //// Done in User32.
1049 if (lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
))
1051 ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1052 KeRosDumpStackFrames(NULL
, 20);
1053 /* This does not happen on Win9x/ME */
1054 SetLastNtError( ERROR_INVALID_PARAMETER
);
1059 * Delete the menu item type when changing type from
1062 if (MenuItem
->fType
!= lpmii
->fType
&&
1063 MENU_ITEM_TYPE(MenuItem
->fType
) == MFT_STRING
)
1065 FreeMenuText(MenuObject
,MenuItem
);
1066 RtlInitUnicodeString(&MenuItem
->lpstr
, NULL
);
1067 MenuItem
->Xlpstr
= NULL
;
1069 if(lpmii
->fType
& MFT_BITMAP
)
1072 MenuItem
->hbmp
= lpmii
->hbmpItem
;
1074 { /* Win 9x/Me stuff */
1075 MenuItem
->hbmp
= (HBITMAP
)((ULONG_PTR
)(LOWORD(lpmii
->dwTypeData
)));
1077 lpmii
->dwTypeData
= 0;
1080 if(lpmii
->fMask
& MIIM_BITMAP
)
1082 MenuItem
->hbmp
= lpmii
->hbmpItem
;
1083 if (MenuItem
->hbmp
<= HBMMENU_POPUP_MINIMIZE
&& MenuItem
->hbmp
>= HBMMENU_CALLBACK
)
1084 MenuItem
->fState
|= MFS_HBMMENUBMP
;
1086 MenuItem
->fState
&= ~MFS_HBMMENUBMP
;
1088 if(lpmii
->fMask
& MIIM_CHECKMARKS
)
1090 MenuItem
->hbmpChecked
= lpmii
->hbmpChecked
;
1091 MenuItem
->hbmpUnchecked
= lpmii
->hbmpUnchecked
;
1093 if(lpmii
->fMask
& MIIM_DATA
)
1095 MenuItem
->dwItemData
= lpmii
->dwItemData
;
1097 if(lpmii
->fMask
& MIIM_ID
)
1099 MenuItem
->wID
= lpmii
->wID
;
1101 if(lpmii
->fMask
& MIIM_STATE
)
1103 /* Remove MFS_DEFAULT flag from all other menu items if this item
1104 has the MFS_DEFAULT state */
1105 if(lpmii
->fState
& MFS_DEFAULT
)
1106 UserSetMenuDefaultItem(MenuObject
, -1, 0);
1107 /* Update the menu item state flags */
1108 UpdateMenuItemState(MenuItem
->fState
, lpmii
->fState
);
1111 if(lpmii
->fMask
& MIIM_SUBMENU
)
1113 if (lpmii
->hSubMenu
)
1115 SubMenuObject
= UserGetMenuObject(lpmii
->hSubMenu
);
1116 if ( SubMenuObject
&& !(UserObjectInDestroy(lpmii
->hSubMenu
)) )
1118 //// wine Bug 12171 : Adding Popup Menu to itself! Could create endless loops.
1120 if (MenuObject
== SubMenuObject
)
1123 ERR("Pop Up Menu Double Trouble!\n");
1124 SubMenuObject
= IntCreateMenu(&hMenu
,
1126 MenuObject
->head
.rpdesk
,
1127 (PPROCESSINFO
)MenuObject
->head
.hTaskWow
); // It will be marked.
1128 if (!SubMenuObject
) return FALSE
;
1129 IntReleaseMenuObject(SubMenuObject
); // This will be referenced again after insertion.
1132 if ( MENU_depth( SubMenuObject
, 0) > MAXMENUDEPTH
)
1134 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1135 if (circref
) IntDestroyMenuObject(SubMenuObject
, FALSE
);
1138 /* Make sure the submenu is marked as a popup menu */
1139 SubMenuObject
->fFlags
|= MNF_POPUP
;
1140 // Now fix the test_subpopup_locked_by_menu tests....
1141 if (MenuItem
->spSubMenu
) IntReleaseMenuObject(MenuItem
->spSubMenu
);
1142 MenuItem
->spSubMenu
= SubMenuObject
;
1143 UserReferenceObject(SubMenuObject
);
1147 EngSetLastError( ERROR_INVALID_PARAMETER
);
1152 { // If submenu just dereference it.
1153 if (MenuItem
->spSubMenu
) IntReleaseMenuObject(MenuItem
->spSubMenu
);
1154 MenuItem
->spSubMenu
= NULL
;
1158 if ((lpmii
->fMask
& MIIM_STRING
) ||
1159 ((lpmii
->fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(lpmii
->fType
) == MF_STRING
)))
1161 /* free the string when used */
1162 FreeMenuText(MenuObject
,MenuItem
);
1163 RtlInitUnicodeString(&MenuItem
->lpstr
, NULL
);
1164 MenuItem
->Xlpstr
= NULL
;
1166 if(lpmii
->dwTypeData
&& lpmii
->cch
&& lpstr
&& lpstr
->Buffer
)
1168 UNICODE_STRING Source
;
1170 Source
.Length
= Source
.MaximumLength
= lpmii
->cch
* sizeof(WCHAR
);
1171 Source
.Buffer
= lpmii
->dwTypeData
;
1173 MenuItem
->lpstr
.Buffer
= DesktopHeapAlloc( MenuObject
->head
.rpdesk
, Source
.Length
+ sizeof(WCHAR
));
1174 if(MenuItem
->lpstr
.Buffer
!= NULL
)
1176 MenuItem
->lpstr
.Length
= 0;
1177 MenuItem
->lpstr
.MaximumLength
= Source
.Length
+ sizeof(WCHAR
);
1178 RtlCopyUnicodeString(&MenuItem
->lpstr
, &Source
);
1179 MenuItem
->lpstr
.Buffer
[MenuItem
->lpstr
.Length
/ sizeof(WCHAR
)] = 0;
1181 MenuItem
->cch
= MenuItem
->lpstr
.Length
/ sizeof(WCHAR
);
1182 MenuItem
->Xlpstr
= (USHORT
*)MenuItem
->lpstr
.Buffer
;
1187 if( !(MenuObject
->fFlags
& MNF_SYSMENU
) &&
1188 !MenuItem
->Xlpstr
&&
1189 !lpmii
->dwTypeData
&&
1190 !(MenuItem
->fType
& MFT_OWNERDRAW
) &&
1192 MenuItem
->fType
|= MFT_SEPARATOR
;
1194 if (sizeof(ROSMENUITEMINFO
) == lpmii
->cbSize
)
1196 MenuItem
->xItem
= lpmii
->Rect
.left
;
1197 MenuItem
->yItem
= lpmii
->Rect
.top
;
1198 MenuItem
->cxItem
= lpmii
->Rect
.right
; // Do this for now......
1199 MenuItem
->cyItem
= lpmii
->Rect
.bottom
;
1200 MenuItem
->dxTab
= lpmii
->dxTab
;
1201 lpmii
->lpstr
= MenuItem
->lpstr
.Buffer
; /* Send back new allocated string or zero */
1202 MenuItem
->cxBmp
= lpmii
->maxBmpSize
.cx
;
1203 MenuItem
->cyBmp
= lpmii
->maxBmpSize
.cy
;
1211 IntEnableMenuItem(PMENU MenuObject
, UINT uIDEnableItem
, UINT uEnable
)
1216 if (!(MenuItem
= MENU_FindItem( &MenuObject
, &uIDEnableItem
, uEnable
))) return (UINT
)-1;
1218 res
= MenuItem
->fState
& (MF_GRAYED
| MF_DISABLED
);
1220 MenuItem
->fState
^= (res
^ uEnable
) & (MF_GRAYED
| MF_DISABLED
);
1222 /* If the close item in the system menu change update the close button */
1225 switch (MenuItem
->wID
) // More than just close.
1233 if (MenuObject
->fFlags
& MNF_SYSSUBMENU
&& MenuObject
->spwndNotify
!= 0)
1235 //RECTL rc = MenuObject->spwndNotify->rcWindow;
1237 /* Refresh the frame to reflect the change */
1238 //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1240 //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1243 UserPaintCaption(MenuObject
->spwndNotify
, DC_BUTTONS
);
1253 IntCheckMenuItem(PMENU MenuObject
, UINT uIDCheckItem
, UINT uCheck
)
1258 if (!(MenuItem
= MENU_FindItem( &MenuObject
, &uIDCheckItem
, uCheck
))) return -1;
1260 res
= (DWORD
)(MenuItem
->fState
& MF_CHECKED
);
1262 MenuItem
->fState
^= (res
^ uCheck
) & MF_CHECKED
;
1268 UserSetMenuDefaultItem(PMENU MenuObject
, UINT uItem
, UINT fByPos
)
1271 PITEM MenuItem
= MenuObject
->rgItems
;
1273 if (!MenuItem
) return FALSE
;
1275 /* reset all default-item flags */
1276 for (i
= 0; i
< MenuObject
->cItems
; i
++, MenuItem
++)
1278 MenuItem
->fState
&= ~MFS_DEFAULT
;
1281 /* no default item */
1282 if(uItem
== (UINT
)-1)
1286 MenuItem
= MenuObject
->rgItems
;
1289 if ( uItem
>= MenuObject
->cItems
) return FALSE
;
1290 MenuItem
[uItem
].fState
|= MFS_DEFAULT
;
1295 for (i
= 0; i
< MenuObject
->cItems
; i
++, MenuItem
++)
1297 if (MenuItem
->wID
== uItem
)
1299 MenuItem
->fState
|= MFS_DEFAULT
;
1309 IntGetMenuDefaultItem(PMENU MenuObject
, UINT fByPos
, UINT gmdiFlags
, DWORD
*gismc
)
1312 PITEM MenuItem
= MenuObject
->rgItems
;
1315 if (!MenuItem
) return -1;
1317 while ( !( MenuItem
->fState
& MFS_DEFAULT
) )
1320 if (i
>= MenuObject
->cItems
) return -1;
1323 /* default: don't return disabled items */
1324 if ( (!(GMDI_USEDISABLED
& gmdiFlags
)) && (MenuItem
->fState
& MFS_DISABLED
)) return -1;
1326 /* search rekursiv when needed */
1327 if ( (gmdiFlags
& GMDI_GOINTOPOPUPS
) && MenuItem
->spSubMenu
)
1331 ret
= IntGetMenuDefaultItem( MenuItem
->spSubMenu
, fByPos
, gmdiFlags
, gismc
);
1333 if ( -1 != ret
) return ret
;
1335 /* when item not found in submenu, return the popup item */
1337 return ( fByPos
) ? i
: MenuItem
->wID
;
1347 if (!(pItem
= MENU_FindItem( &pMenu
, (UINT
*)&nPos
, MF_BYPOSITION
))) return NULL
;
1348 return pItem
->spSubMenu
;
1351 /***********************************************************************
1352 * MenuInitSysMenuPopup
1354 * Grey the appropriate items in System menu.
1356 void FASTCALL
MENU_InitSysMenuPopup(PMENU menu
, DWORD style
, DWORD clsStyle
, LONG HitTest
)
1361 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1362 IntEnableMenuItem( menu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1363 gray
= ((style
& WS_MAXIMIZE
) != 0);
1364 IntEnableMenuItem( menu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1365 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
1366 IntEnableMenuItem( menu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1367 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
1368 IntEnableMenuItem( menu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1369 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1370 IntEnableMenuItem( menu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1371 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
1373 /* The menu item must keep its state if it's disabled */
1375 IntEnableMenuItem( menu
, SC_CLOSE
, MF_GRAYED
);
1377 /* Set default menu item */
1378 if(style
& WS_MINIMIZE
) DefItem
= SC_RESTORE
;
1379 else if(HitTest
== HTCAPTION
) DefItem
= ((style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
1380 else DefItem
= SC_CLOSE
;
1382 UserSetMenuDefaultItem(menu
, DefItem
, MF_BYCOMMAND
);
1386 /***********************************************************************
1387 * MenuDrawPopupGlyph
1389 * Draws popup magic glyphs (can be found in system menu).
1391 static void FASTCALL
1392 MENU_DrawPopupGlyph(HDC dc
, LPRECT r
, INT_PTR popupMagic
, BOOL inactive
, BOOL hilite
)
1395 HFONT hFont
, hOldFont
;
1401 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1404 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1407 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1410 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1414 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic
);
1417 RtlZeroMemory(&lf
, sizeof(LOGFONTW
));
1418 RECTL_vInflateRect(r
, -2, -2);
1419 lf
.lfHeight
= r
->bottom
- r
->top
;
1421 lf
.lfWeight
= FW_NORMAL
;
1422 lf
.lfCharSet
= DEFAULT_CHARSET
;
1423 RtlCopyMemory(lf
.lfFaceName
, L
"Marlett", sizeof(L
"Marlett"));
1424 hFont
= GreCreateFontIndirectW(&lf
);
1425 /* save font and text color */
1426 hOldFont
= NtGdiSelectFont(dc
, hFont
);
1427 clrsave
= GreGetTextColor(dc
);
1428 bkmode
= GreGetBkMode(dc
);
1429 /* set color and drawing mode */
1430 IntGdiSetBkMode(dc
, TRANSPARENT
);
1436 IntGdiSetTextColor(dc
, IntGetSysColor(COLOR_HIGHLIGHTTEXT
));
1437 GreTextOutW(dc
, r
->left
+ 1, r
->top
+ 1, &symbol
, 1);
1440 IntGdiSetTextColor(dc
, IntGetSysColor(inactive
? COLOR_GRAYTEXT
: (hilite
? COLOR_HIGHLIGHTTEXT
: COLOR_MENUTEXT
)));
1441 /* draw selected symbol */
1442 GreTextOutW(dc
, r
->left
, r
->top
, &symbol
, 1);
1443 /* restore previous settings */
1444 IntGdiSetTextColor(dc
, clrsave
);
1445 NtGdiSelectFont(dc
, hOldFont
);
1446 IntGdiSetBkMode(dc
, bkmode
);
1447 GreDeleteObject(hFont
);
1450 /***********************************************************************
1451 * MENU_AdjustMenuItemRect
1453 * Adjust menu item rectangle according to scrolling state.
1456 MENU_AdjustMenuItemRect(PMENU menu
, PRECTL rect
)
1458 if (menu
->dwArrowsOn
)
1460 UINT arrow_bitmap_height
;
1461 arrow_bitmap_height
= gpsi
->oembmi
[OBI_UPARROW
].cy
; ///// Menu up arrow! OBM_UPARROW
1462 rect
->top
+= arrow_bitmap_height
- menu
->iTop
;
1463 rect
->bottom
+= arrow_bitmap_height
- menu
->iTop
;
1467 /***********************************************************************
1468 * MENU_FindItemByCoords
1470 * Find the item at the specified coordinates (screen coords). Does
1471 * not work for child windows and therefore should not be called for
1472 * an arbitrary system menu.
1474 static ITEM
*MENU_FindItemByCoords( MENU
*menu
, POINT pt
, UINT
*pos
)
1479 PWND pWnd
= ValidateHwndNoErr(menu
->hWnd
);
1481 if (!IntGetWindowRect(pWnd
, &rect
)) return NULL
;
1482 if (pWnd
->ExStyle
& WS_EX_LAYOUTRTL
)
1483 pt
.x
= rect
.right
- 1 - pt
.x
;
1487 item
= menu
->rgItems
;
1488 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
1490 //rect = item->rect;
1491 rect
.left
= item
->xItem
;
1492 rect
.top
= item
->yItem
;
1493 rect
.right
= item
->cxItem
; // Do this for now......
1494 rect
.bottom
= item
->cyItem
;
1496 MENU_AdjustMenuItemRect(menu
, &rect
);
1497 if (RECTL_bPointInRect(&rect
, pt
.x
, pt
.y
))
1506 INT FASTCALL
IntMenuItemFromPoint(PWND pWnd
, HMENU hMenu
, POINT ptScreen
)
1508 MENU
*menu
= UserGetMenuObject(hMenu
);
1511 /*FIXME: Do we have to handle hWnd here? */
1512 if (!menu
) return -1;
1513 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
1517 /***********************************************************************
1520 * Find the menu item selected by a key press.
1521 * Return item id, -1 if none, -2 if we should close the menu.
1523 static UINT FASTCALL
MENU_FindItemByKey(PWND WndOwner
, PMENU menu
,
1524 WCHAR Key
, BOOL ForceMenuChar
)
1529 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key
, Key
, menu
);
1531 if (!menu
|| !VerifyMenu(menu
))
1532 menu
= co_IntGetSubMenu( UserGetMenuObject(WndOwner
->SystemMenu
), 0 );
1535 ITEM
*item
= menu
->rgItems
;
1537 if ( !ForceMenuChar
)
1540 BOOL cjk
= UserGetSystemMetrics( SM_DBCSENABLED
);
1542 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
1544 LPWSTR text
= item
->Xlpstr
;
1547 const WCHAR
*p
= text
- 2;
1550 const WCHAR
*q
= p
+ 2;
1551 p
= wcschr (q
, '&');
1552 if (!p
&& cjk
) p
= wcschr (q
, '\036'); /* Japanese Win16 */
1554 while (p
!= NULL
&& p
[1] == '&');
1555 if (p
&& (towupper(p
[1]) == towupper(Key
))) return i
;
1560 Flags
|= menu
->fFlags
& MNF_POPUP
? MF_POPUP
: 0;
1561 Flags
|= menu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0;
1563 MenuChar
= co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_MENUCHAR
,
1564 MAKEWPARAM(Key
, Flags
), (LPARAM
) UserHMGetHandle(menu
));
1565 if (HIWORD(MenuChar
) == MNC_EXECUTE
) return LOWORD(MenuChar
);
1566 if (HIWORD(MenuChar
) == MNC_CLOSE
) return (UINT
)(-2);
1571 /***********************************************************************
1572 * MenuGetBitmapItemSize
1574 * Get the size of a bitmap item.
1576 static void FASTCALL
MENU_GetBitmapItemSize(PITEM lpitem
, SIZE
*size
, PWND WndOwner
)
1579 HBITMAP bmp
= lpitem
->hbmp
;
1581 size
->cx
= size
->cy
= 0;
1583 /* check if there is a magic menu item associated with this item */
1584 if (IS_MAGIC_BITMAP(bmp
))
1586 switch((INT_PTR
) bmp
)
1588 case (INT_PTR
)HBMMENU_CALLBACK
:
1590 MEASUREITEMSTRUCT measItem
;
1591 measItem
.CtlType
= ODT_MENU
;
1593 measItem
.itemID
= lpitem
->wID
;
1594 measItem
.itemWidth
= lpitem
->cxItem
- lpitem
->xItem
; //lpitem->Rect.right - lpitem->Rect.left;
1595 measItem
.itemHeight
= lpitem
->cyItem
- lpitem
->yItem
; //lpitem->Rect.bottom - lpitem->Rect.top;
1596 measItem
.itemData
= lpitem
->dwItemData
;
1597 co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
1598 size
->cx
= measItem
.itemWidth
;
1599 size
->cy
= measItem
.itemHeight
;
1600 TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem
.itemHeight
,measItem
.itemWidth
);
1605 case (INT_PTR
) HBMMENU_SYSTEM
:
1606 if (lpitem
->dwItemData
)
1608 bmp
= (HBITMAP
) lpitem
->dwItemData
;
1612 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
1613 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
1614 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
1615 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
1616 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
1617 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1618 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1619 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1620 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1621 /* FIXME: Why we need to subtract these magic values? */
1622 /* to make them smaller than the menu bar? */
1623 size
->cx
= UserGetSystemMetrics(SM_CXSIZE
) - 2;
1624 size
->cy
= UserGetSystemMetrics(SM_CYSIZE
) - 4;
1629 if (GreGetObject(bmp
, sizeof(BITMAP
), &bm
))
1631 size
->cx
= bm
.bmWidth
;
1632 size
->cy
= bm
.bmHeight
;
1636 /***********************************************************************
1637 * MenuDrawBitmapItem
1639 * Draw a bitmap item.
1641 static void FASTCALL
MENU_DrawBitmapItem(HDC hdc
, PITEM lpitem
, const RECT
*rect
,
1642 PMENU Menu
, PWND WndOwner
, UINT odaction
, BOOL MenuBar
)
1648 int w
= rect
->right
- rect
->left
;
1649 int h
= rect
->bottom
- rect
->top
;
1650 int bmp_xoffset
= 0;
1652 HBITMAP hbmToDraw
= lpitem
->hbmp
;
1655 /* Check if there is a magic menu item associated with this item */
1656 if (IS_MAGIC_BITMAP(hbmToDraw
))
1662 switch ((INT_PTR
)hbmToDraw
)
1664 case (INT_PTR
)HBMMENU_SYSTEM
:
1665 if (lpitem
->dwItemData
)
1667 if (ValidateHwndNoErr((HWND
)lpitem
->dwItemData
))
1669 ERR("Get Item Data from this Window!!!\n");
1672 ERR("Draw Bitmap\n");
1673 bmp
= (HBITMAP
)lpitem
->dwItemData
;
1674 if (!GreGetObject( bmp
, sizeof(bm
), &bm
)) return;
1678 PCURICON_OBJECT pIcon
= NULL
;
1679 //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1681 //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1682 /* only use right half of the bitmap */
1683 //bmp_xoffset = bm.bmWidth / 2;
1684 //bm.bmWidth -= bmp_xoffset;
1687 pIcon
= NC_IconForWindow(WndOwner
);
1688 // FIXME: NC_IconForWindow should reference it for us */
1689 if (pIcon
) UserReferenceObject(pIcon
);
1694 LONG cx
= UserGetSystemMetrics(SM_CXSMICON
);
1695 LONG cy
= UserGetSystemMetrics(SM_CYSMICON
);
1696 LONG x
= rect
->left
- cx
/2 + 1 + (rect
->bottom
- rect
->top
)/2; // this is really what Window does
1697 LONG y
= (rect
->top
+ rect
->bottom
)/2 - cy
/2; // center
1698 UserDrawIconEx(hdc
, x
, y
, pIcon
, cx
, cy
, 0, NULL
, DI_NORMAL
);
1699 UserDereferenceObject(pIcon
);
1704 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
1705 flags
= DFCS_CAPTIONRESTORE
;
1707 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
1709 flags
= DFCS_CAPTIONMIN
;
1711 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
1713 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
1715 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
1716 flags
= DFCS_CAPTIONCLOSE
;
1718 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
1719 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
1721 case (INT_PTR
)HBMMENU_CALLBACK
:
1723 DRAWITEMSTRUCT drawItem
;
1725 drawItem
.CtlType
= ODT_MENU
;
1727 drawItem
.itemID
= lpitem
->wID
;
1728 drawItem
.itemAction
= odaction
;
1729 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
1730 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
1731 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
1732 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
1733 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
1734 drawItem
.itemState
|= (!(Menu
->fFlags
& MNF_UNDERLINE
))?ODS_NOACCEL
:0;
1735 drawItem
.itemState
|= (Menu
->fFlags
& MNF_INACTIVE
)?ODS_INACTIVE
:0;
1736 drawItem
.hwndItem
= (HWND
)UserHMGetHandle(Menu
);
1738 drawItem
.rcItem
= *rect
;
1739 drawItem
.itemData
= lpitem
->dwItemData
;
1740 /* some applications make this assumption on the DC's origin */
1741 GreSetViewportOrgEx( hdc
, lpitem
->xItem
, lpitem
->yItem
, &origorg
);
1742 RECTL_vOffsetRect( &drawItem
.rcItem
, - lpitem
->xItem
, - lpitem
->yItem
);
1743 co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
1744 GreSetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1749 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1750 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1751 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1752 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1753 MENU_DrawPopupGlyph(hdc
, &r
, (INT_PTR
)hbmToDraw
, lpitem
->fState
& MF_GRAYED
, lpitem
->fState
& MF_HILITE
);
1756 RECTL_vInflateRect(&r
, -1, -1);
1757 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
1758 DrawFrameControl(hdc
, &r
, DFC_CAPTION
, flags
);
1762 if (!bmp
|| !GreGetObject( bmp
, sizeof(bm
), &bm
)) return;
1765 hdcMem
= NtGdiCreateCompatibleDC( hdc
);
1766 NtGdiSelectBitmap( hdcMem
, bmp
);
1767 /* handle fontsize > bitmap_height */
1768 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
1770 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
1771 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmp
)
1772 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHT
));
1773 NtGdiBitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
, 0, 0);
1774 IntGdiDeleteDC( hdcMem
, FALSE
);
1778 IntGetDialogBaseUnits(VOID
)
1787 if ((hdc
= UserGetDCEx(NULL
, NULL
, DCX_CACHE
)))
1789 size
.cx
= IntGetCharDimensions( hdc
, NULL
, (PDWORD
)&size
.cy
);
1790 if (size
.cx
) units
= MAKELONG( size
.cx
, size
.cy
);
1791 UserReleaseDC( 0, hdc
, FALSE
);
1798 /***********************************************************************
1801 * Calculate the size of the menu item and store it in lpitem->rect.
1803 static void FASTCALL
MENU_CalcItemSize( HDC hdc
, PITEM lpitem
, PMENU Menu
, PWND pwndOwner
,
1804 INT orgX
, INT orgY
, BOOL menuBar
, BOOL textandbmp
)
1807 UINT check_bitmap_width
= UserGetSystemMetrics( SM_CXMENUCHECK
);
1808 UINT arrow_bitmap_width
;
1812 TRACE("dc=%x owner=%x (%d,%d)\n", hdc
, pwndOwner
, orgX
, orgY
);
1814 arrow_bitmap_width
= gpsi
->oembmi
[OBI_MNARROW
].cx
;
1816 MenuCharSize
.cx
= IntGetCharDimensions( hdc
, NULL
, (PDWORD
)&MenuCharSize
.cy
);
1818 RECTL_vSetRect( &Rect
, orgX
, orgY
, orgX
, orgY
);
1820 if (lpitem
->fType
& MF_OWNERDRAW
)
1822 MEASUREITEMSTRUCT mis
;
1823 mis
.CtlType
= ODT_MENU
;
1825 mis
.itemID
= lpitem
->wID
;
1826 mis
.itemData
= lpitem
->dwItemData
;
1827 mis
.itemHeight
= HIWORD( IntGetDialogBaseUnits());
1829 co_IntSendMessage( UserHMGetHandle(pwndOwner
), WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1830 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1831 * width of a menufont character to the width of an owner-drawn menu.
1833 Rect
.right
+= mis
.itemWidth
+ 2 * MenuCharSize
.cx
;
1835 /* under at least win95 you seem to be given a standard
1836 height for the menu and the height value is ignored */
1837 Rect
.bottom
+= UserGetSystemMetrics(SM_CYMENUSIZE
);
1839 Rect
.bottom
+= mis
.itemHeight
;
1841 //lpitem->cxBmp = mis.itemWidth;
1842 //lpitem->cyBmp = mis.itemHeight;
1843 TRACE("MF_OWNERDRAW Height %d Width %d\n",mis
.itemHeight
,mis
.itemWidth
);
1844 TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1845 lpitem
->wID
, Rect
.right
-Rect
.left
,
1846 Rect
.bottom
-Rect
.top
, MenuCharSize
.cx
, MenuCharSize
.cy
);
1848 lpitem
->xItem
= Rect
.left
;
1849 lpitem
->yItem
= Rect
.top
;
1850 lpitem
->cxItem
= Rect
.right
;
1851 lpitem
->cyItem
= Rect
.bottom
;
1856 lpitem
->xItem
= orgX
;
1857 lpitem
->yItem
= orgY
;
1858 lpitem
->cxItem
= orgX
;
1859 lpitem
->cyItem
= orgY
;
1861 if (lpitem
->fType
& MF_SEPARATOR
)
1863 lpitem
->cyItem
+= UserGetSystemMetrics( SM_CYMENUSIZE
)/2;//SEPARATOR_HEIGHT;
1865 lpitem
->cxItem
+= arrow_bitmap_width
+ MenuCharSize
.cx
;
1876 MENU_GetBitmapItemSize(lpitem
, &size
, pwndOwner
);
1877 /* Keep the size of the bitmap in callback mode to be able
1878 * to draw it correctly */
1879 lpitem
->cxBmp
= size
.cx
;
1880 lpitem
->cyBmp
= size
.cy
;
1881 Menu
->cxTextAlign
= max(Menu
->cxTextAlign
, size
.cx
);
1882 lpitem
->cxItem
+= size
.cx
+ 2;
1883 itemheight
= size
.cy
+ 2;
1885 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
1886 lpitem
->cxItem
+= 2 * check_bitmap_width
;
1887 lpitem
->cxItem
+= 4 + MenuCharSize
.cx
;
1888 lpitem
->dxTab
= lpitem
->cxItem
;
1889 lpitem
->cxItem
+= arrow_bitmap_width
;
1890 } else /* hbmpItem & MenuBar */ {
1891 MENU_GetBitmapItemSize(lpitem
, &size
, pwndOwner
);
1892 lpitem
->cxItem
+= size
.cx
;
1893 if( lpitem
->Xlpstr
) lpitem
->cxItem
+= 2;
1894 itemheight
= size
.cy
;
1896 /* Special case: Minimize button doesn't have a space behind it. */
1897 if (lpitem
->hbmp
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1898 lpitem
->hbmp
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1899 lpitem
->cxItem
-= 1;
1902 else if (!menuBar
) {
1903 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
1904 lpitem
->cxItem
+= check_bitmap_width
;
1905 lpitem
->cxItem
+= 4 + MenuCharSize
.cx
;
1906 lpitem
->dxTab
= lpitem
->cxItem
;
1907 lpitem
->cxItem
+= arrow_bitmap_width
;
1910 /* it must be a text item - unless it's the system menu */
1911 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->Xlpstr
) {
1912 HFONT hfontOld
= NULL
;
1913 RECT rc
;// = lpitem->Rect;
1914 LONG txtheight
, txtwidth
;
1916 rc
.left
= lpitem
->xItem
;
1917 rc
.top
= lpitem
->yItem
;
1918 rc
.right
= lpitem
->cxItem
; // Do this for now......
1919 rc
.bottom
= lpitem
->cyItem
;
1921 if ( lpitem
->fState
& MFS_DEFAULT
) {
1922 hfontOld
= NtGdiSelectFont( hdc
, ghMenuFontBold
);
1925 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, -1, &rc
, DT_SINGLELINE
|DT_CALCRECT
);
1927 lpitem
->cxItem
+= rc
.right
- rc
.left
;
1928 itemheight
= max( max( itemheight
, txtheight
), UserGetSystemMetrics( SM_CYMENU
) - 1);
1930 lpitem
->cxItem
+= 2 * MenuCharSize
.cx
;
1932 if ((p
= wcschr( lpitem
->Xlpstr
, '\t' )) != NULL
) {
1935 int n
= (int)( p
- lpitem
->Xlpstr
);
1936 /* Item contains a tab (only meaningful in popup menus) */
1937 /* get text size before the tab */
1938 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, n
, &rc
,
1939 DT_SINGLELINE
|DT_CALCRECT
);
1940 txtwidth
= rc
.right
- rc
.left
;
1941 p
+= 1; /* advance past the Tab */
1942 /* get text size after the tab */
1943 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1944 DT_SINGLELINE
|DT_CALCRECT
);
1945 lpitem
->dxTab
+= txtwidth
;
1946 txtheight
= max( txtheight
, tmpheight
);
1947 txtwidth
+= MenuCharSize
.cx
+ /* space for the tab */
1948 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1950 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, -1, &rc
,
1951 DT_SINGLELINE
|DT_CALCRECT
);
1952 txtwidth
= rc
.right
- rc
.left
;
1953 lpitem
->dxTab
+= txtwidth
;
1955 lpitem
->cxItem
+= 2 + txtwidth
;
1956 itemheight
= max( itemheight
,
1957 max( txtheight
+ 2, MenuCharSize
.cy
+ 4));
1961 NtGdiSelectFont (hdc
, hfontOld
);
1963 } else if( menuBar
) {
1964 itemheight
= max( itemheight
, UserGetSystemMetrics(SM_CYMENU
)-1);
1966 lpitem
->cyItem
+= itemheight
;
1967 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->xItem
, lpitem
->yItem
, lpitem
->cxItem
, lpitem
->cyItem
);
1970 /***********************************************************************
1971 * MENU_GetMaxPopupHeight
1974 MENU_GetMaxPopupHeight(PMENU lppop
)
1978 //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1979 return lppop
->cyMax
;
1981 //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
1982 return UserGetSystemMetrics(SM_CYSCREEN
) - UserGetSystemMetrics(SM_CYBORDER
);
1985 /***********************************************************************
1986 * MenuPopupMenuCalcSize
1988 * Calculate the size of a popup menu.
1990 static void FASTCALL
MENU_PopupMenuCalcSize(PMENU Menu
, PWND WndOwner
)
1995 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1996 BOOL textandbmp
= FALSE
;
1998 Menu
->cxMenu
= Menu
->cyMenu
= 0;
1999 if (Menu
->cItems
== 0) return;
2001 hdc
= UserGetDCEx(NULL
, NULL
, DCX_CACHE
);
2003 NtGdiSelectFont( hdc
, ghMenuFont
);
2008 Menu
->cxTextAlign
= 0;
2010 while (start
< Menu
->cItems
)
2012 lpitem
= &Menu
->rgItems
[start
];
2014 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
2015 orgX
+= MENU_COL_SPACE
;
2016 orgY
= MENU_TOP_MARGIN
;
2018 maxTab
= maxTabWidth
= 0;
2019 /* Parse items until column break or end of menu */
2020 for (i
= start
; i
< Menu
->cItems
; i
++, lpitem
++)
2023 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
2025 MENU_CalcItemSize(hdc
, lpitem
, Menu
, WndOwner
, orgX
, orgY
, FALSE
, textandbmp
);
2026 maxX
= max(maxX
, lpitem
->cxItem
);
2027 orgY
= lpitem
->cyItem
;
2028 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->dxTab
)
2030 maxTab
= max( maxTab
, lpitem
->dxTab
);
2031 maxTabWidth
= max(maxTabWidth
, lpitem
->cxItem
- lpitem
->dxTab
);
2033 if( lpitem
->Xlpstr
&& lpitem
->hbmp
) textandbmp
= TRUE
;
2036 /* Finish the column (set all items to the largest width found) */
2037 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
2038 for (lpitem
= &Menu
->rgItems
[start
]; start
< i
; start
++, lpitem
++)
2040 lpitem
->cxItem
= maxX
;
2041 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->dxTab
)
2042 lpitem
->dxTab
= maxTab
;
2044 Menu
->cyMenu
= max(Menu
->cyMenu
, orgY
);
2047 Menu
->cxMenu
= maxX
;
2048 /* if none of the items have both text and bitmap then
2049 * the text and bitmaps are all aligned on the left. If there is at
2050 * least one item with both text and bitmap then bitmaps are
2051 * on the left and texts left aligned with the right hand side
2053 if( !textandbmp
) Menu
->cxTextAlign
= 0;
2055 /* space for 3d border */
2056 Menu
->cyMenu
+= MENU_BOTTOM_MARGIN
;
2059 /* Adjust popup height if it exceeds maximum */
2060 maxHeight
= MENU_GetMaxPopupHeight(Menu
);
2061 Menu
->iMaxTop
= Menu
->cyMenu
- MENU_TOP_MARGIN
;
2062 if (Menu
->cyMenu
>= maxHeight
)
2064 Menu
->cyMenu
= maxHeight
;
2065 Menu
->dwArrowsOn
= 1;
2069 Menu
->dwArrowsOn
= 0;
2071 UserReleaseDC( 0, hdc
, FALSE
);
2074 /***********************************************************************
2075 * MENU_MenuBarCalcSize
2077 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2078 * height is off by 1 pixel which causes lengthy window relocations when
2079 * active document window is maximized/restored.
2081 * Calculate the size of the menu bar.
2083 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
, PMENU lppop
, PWND pwndOwner
)
2086 UINT start
, i
, helpPos
;
2087 int orgX
, orgY
, maxY
;
2089 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
2090 if (lppop
->cItems
== 0) return;
2091 //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2092 lppop
->cxMenu
= lprect
->right
- lprect
->left
;
2094 maxY
= lprect
->top
+1;
2097 lppop
->cxTextAlign
= 0;
2098 while (start
< lppop
->cItems
)
2100 lpitem
= &lppop
->rgItems
[start
];
2101 orgX
= lprect
->left
;
2104 /* Parse items until line break or end of menu */
2105 for (i
= start
; i
< lppop
->cItems
; i
++, lpitem
++)
2107 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
2109 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
2111 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
2112 //debug_print_menuitem (" item: ", lpitem, "");
2113 //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2114 MENU_CalcItemSize(hdc
, lpitem
, lppop
, pwndOwner
, orgX
, orgY
, TRUE
, FALSE
);
2116 if (lpitem
->cxItem
> lprect
->right
)
2118 if (i
!= start
) break;
2119 else lpitem
->cxItem
= lprect
->right
;
2121 maxY
= max( maxY
, lpitem
->cyItem
);
2122 orgX
= lpitem
->cxItem
;
2125 /* Finish the line (set all items to the largest height found) */
2127 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2128 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2130 while (start
< i
) lppop
->rgItems
[start
++].cyItem
= maxY
;
2132 start
= i
; /* This works! */
2135 lprect
->bottom
= maxY
;
2136 lppop
->cyMenu
= lprect
->bottom
- lprect
->top
;
2138 /* Flush right all items between the MF_RIGHTJUSTIFY and */
2139 /* the last item (if several lines, only move the last line) */
2140 if (helpPos
== ~0U) return;
2141 lpitem
= &lppop
->rgItems
[lppop
->cItems
-1];
2142 orgY
= lpitem
->yItem
;
2143 orgX
= lprect
->right
;
2144 for (i
= lppop
->cItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
2145 if (lpitem
->yItem
!= orgY
) break; /* Other line */
2146 if (lpitem
->cxItem
>= orgX
) break; /* Too far right already */
2147 lpitem
->xItem
+= orgX
- lpitem
->cxItem
;
2148 lpitem
->cxItem
= orgX
;
2149 orgX
= lpitem
->xItem
;
2153 /***********************************************************************
2154 * MENU_DrawScrollArrows
2156 * Draw scroll arrows.
2158 static void MENU_DrawScrollArrows(PMENU lppop
, HDC hdc
)
2160 UINT arrow_bitmap_width
, arrow_bitmap_height
;
2164 arrow_bitmap_width
= gpsi
->oembmi
[OBI_DNARROW
].cx
;
2165 arrow_bitmap_height
= gpsi
->oembmi
[OBI_DNARROW
].cy
;
2169 rect
.right
= lppop
->cxMenu
;
2170 rect
.bottom
= arrow_bitmap_height
;
2171 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENU
));
2172 dfcrc
.left
= (lppop
->cxMenu
- arrow_bitmap_width
) / 2;
2174 dfcrc
.right
= arrow_bitmap_width
;
2175 dfcrc
.bottom
= arrow_bitmap_height
;
2176 DrawFrameControl(hdc
, &dfcrc
, DFC_MENU
, (lppop
->iTop
? 0 : DFCS_INACTIVE
)|DFCS_MENUARROWUP
);
2178 rect
.top
= lppop
->cyMenu
- arrow_bitmap_height
;
2179 rect
.bottom
= lppop
->cyMenu
;
2180 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENU
));
2181 if (!(lppop
->iTop
< lppop
->iMaxTop
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
)))
2182 Flags
= DFCS_INACTIVE
;
2183 dfcrc
.left
= (lppop
->cxMenu
- arrow_bitmap_width
) / 2;
2184 dfcrc
.top
= lppop
->cyMenu
- arrow_bitmap_height
;
2185 dfcrc
.right
= arrow_bitmap_width
;
2186 dfcrc
.bottom
= lppop
->cyMenu
;
2187 DrawFrameControl(hdc
, &dfcrc
, DFC_MENU
, Flags
|DFCS_MENUARROWDOWN
);
2190 /***********************************************************************
2193 * Draw a single menu item.
2195 static void FASTCALL
MENU_DrawMenuItem(PWND Wnd
, PMENU Menu
, PWND WndOwner
, HDC hdc
,
2196 PITEM lpitem
, UINT Height
, BOOL menuBar
, UINT odaction
)
2200 BOOL flat_menu
= FALSE
;
2202 UINT arrow_bitmap_width
= 0;
2205 arrow_bitmap_width
= gpsi
->oembmi
[OBI_MNARROW
].cx
;
2208 if (lpitem
->fType
& MF_SYSMENU
)
2210 if (!(Wnd
->style
& WS_MINIMIZE
))
2212 NC_GetInsideRect(Wnd
, &rect
);
2213 UserDrawSysMenuButton(Wnd
, hdc
, &rect
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
2218 UserSystemParametersInfo (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2219 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
2223 if (lpitem
->fState
& MF_HILITE
)
2225 if(menuBar
&& !flat_menu
) {
2226 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_MENUTEXT
));
2227 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_MENU
));
2229 if (lpitem
->fState
& MF_GRAYED
)
2230 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_GRAYTEXT
));
2232 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHTTEXT
));
2233 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHT
));
2238 if (lpitem
->fState
& MF_GRAYED
)
2239 IntGdiSetTextColor( hdc
, IntGetSysColor( COLOR_GRAYTEXT
) );
2241 IntGdiSetTextColor( hdc
, IntGetSysColor( COLOR_MENUTEXT
) );
2242 IntGdiSetBkColor( hdc
, IntGetSysColor( bkgnd
) );
2245 //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2246 //rect = lpitem->Rect;
2247 rect
.left
= lpitem
->xItem
;
2248 rect
.top
= lpitem
->yItem
;
2249 rect
.right
= lpitem
->cxItem
; // Do this for now......
2250 rect
.bottom
= lpitem
->cyItem
;
2252 MENU_AdjustMenuItemRect(Menu
, &rect
);
2254 if (lpitem
->fType
& MF_OWNERDRAW
)
2257 ** Experimentation under Windows reveals that an owner-drawn
2258 ** menu is given the rectangle which includes the space it requested
2259 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2260 ** and a popup-menu arrow. This is the value of lpitem->rect.
2261 ** Windows will leave all drawing to the application except for
2262 ** the popup-menu arrow. Windows always draws that itself, after
2263 ** the menu owner has finished drawing.
2266 COLORREF old_bk
, old_text
;
2268 dis
.CtlType
= ODT_MENU
;
2270 dis
.itemID
= lpitem
->wID
;
2271 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
2273 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
2274 if (lpitem
->fState
& MF_DEFAULT
) dis
.itemState
|= ODS_DEFAULT
;
2275 if (lpitem
->fState
& MF_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
2276 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
2277 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
2278 if (!(Menu
->fFlags
& MNF_UNDERLINE
)) dis
.itemState
|= ODS_NOACCEL
;
2279 if (Menu
->fFlags
& MNF_INACTIVE
) dis
.itemState
|= ODS_INACTIVE
;
2280 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2281 dis
.hwndItem
= (HWND
) UserHMGetHandle(Menu
);
2284 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2285 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
2286 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
2287 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
2289 TRACE("Ownerdraw: Width %d Height %d\n", dis
.rcItem
.right
-dis
.rcItem
.left
, dis
.rcItem
.bottom
-dis
.rcItem
.top
);
2290 old_bk
= GreGetBkColor(hdc
);
2291 old_text
= GreGetTextColor(hdc
);
2292 co_IntSendMessage(UserHMGetHandle(WndOwner
), WM_DRAWITEM
, 0, (LPARAM
) &dis
);
2293 IntGdiSetBkColor(hdc
, old_bk
);
2294 IntGdiSetTextColor(hdc
, old_text
);
2295 /* Draw the popup-menu arrow */
2296 if (!menuBar
&& lpitem
->spSubMenu
)
2299 RtlCopyMemory(&rectTemp
, &rect
, sizeof(RECT
));
2300 rectTemp
.left
= rectTemp
.right
- UserGetSystemMetrics(SM_CXMENUCHECK
);
2301 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
2306 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
2308 if (lpitem
->fState
& MF_HILITE
)
2312 RECTL_vInflateRect (&rect
, -1, -1);
2313 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENUHILIGHT
));
2314 RECTL_vInflateRect (&rect
, 1, 1);
2315 FrameRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_HIGHLIGHT
));
2320 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
2322 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_HIGHLIGHT
));
2326 FillRect( hdc
, &rect
, IntGetSysColorBrush(bkgnd
) );
2328 IntGdiSetBkMode( hdc
, TRANSPARENT
);
2330 /* vertical separator */
2331 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
2336 rc
.left
-= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2338 rc
.bottom
= Height
- 3;
2341 oldPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject(DC_PEN
) );
2342 IntSetDCPenColor(hdc
, IntGetSysColor(COLOR_BTNSHADOW
));
2343 GreMoveTo( hdc
, rc
.left
, rc
.top
, NULL
);
2344 NtGdiLineTo( hdc
, rc
.left
, rc
.bottom
);
2345 NtGdiSelectPen( hdc
, oldPen
);
2348 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
2351 /* horizontal separator */
2352 if (lpitem
->fType
& MF_SEPARATOR
)
2359 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
2362 oldPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject(DC_PEN
) );
2363 IntSetDCPenColor( hdc
, IntGetSysColor(COLOR_BTNSHADOW
));
2364 GreMoveTo( hdc
, rc
.left
, rc
.top
, NULL
);
2365 NtGdiLineTo( hdc
, rc
.right
, rc
.top
);
2366 NtGdiSelectPen( hdc
, oldPen
);
2369 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
2374 /* helper lines for debugging */
2375 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2376 FrameRect(hdc
, &rect
, NtGdiGetStockObject(BLACK_BRUSH
));
2377 NtGdiSelectPen(hdc
, NtGdiGetStockObject(DC_PEN
));
2378 IntSetDCPenColor(hdc
, IntGetSysColor(COLOR_WINDOWFRAME
));
2379 GreMoveTo(hdc
, rect
.left
, (rect
.top
+ rect
.bottom
) / 2, NULL
);
2380 NtGdiLineTo(hdc
, rect
.right
, (rect
.top
+ rect
.bottom
) / 2);
2386 INT y
= rect
.top
+ rect
.bottom
;
2388 BOOL checked
= FALSE
;
2389 UINT check_bitmap_width
= UserGetSystemMetrics( SM_CXMENUCHECK
);
2390 UINT check_bitmap_height
= UserGetSystemMetrics( SM_CYMENUCHECK
);
2391 /* Draw the check mark
2394 * Custom checkmark bitmaps are monochrome but not always 1bpp.
2396 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
)) {
2397 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hbmpChecked
:
2398 lpitem
->hbmpUnchecked
;
2399 if (bm
) /* we have a custom bitmap */
2401 HDC hdcMem
= NtGdiCreateCompatibleDC( hdc
);
2403 NtGdiSelectBitmap( hdcMem
, bm
);
2404 NtGdiBitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
2405 check_bitmap_width
, check_bitmap_height
,
2406 hdcMem
, 0, 0, SRCCOPY
, 0,0);
2407 IntGdiDeleteDC( hdcMem
, FALSE
);
2410 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
2414 r
.right
= r
.left
+ UserGetSystemMetrics(SM_CXMENUCHECK
);
2415 DrawFrameControl( hdc
, &r
, DFC_MENU
,
2416 (lpitem
->fType
& MFT_RADIOCHECK
) ?
2417 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
2421 if ( lpitem
->hbmp
)//&& !( checked && (Menu->dwStyle & MNS_CHECKORBMP)))
2424 //CopyRect(&bmpRect, &rect);
2426 if (!((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
) && !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
2427 bmpRect
.left
+= check_bitmap_width
+ 2;
2428 if (!(checked
&& ((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
)))
2431 bmpRect
.right
= bmpRect
.left
+ lpitem
->cxBmp
;
2432 /* some applications make this assumption on the DC's origin */
2433 //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
2434 MENU_DrawBitmapItem(hdc
, lpitem
, &bmpRect
, Menu
, WndOwner
, odaction
, menuBar
);
2435 //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
2438 /* Draw the popup-menu arrow */
2439 if (lpitem
->spSubMenu
)
2442 RtlCopyMemory(&rectTemp
, &rect
, sizeof(RECT
));
2443 rectTemp
.left
= rectTemp
.right
- UserGetSystemMetrics(SM_CXMENUCHECK
);
2444 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
2447 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
2448 rect
.left
+= check_bitmap_width
;
2449 rect
.right
-= arrow_bitmap_width
;//check_bitmap_width;
2451 else if( lpitem
->hbmp
)
2452 { /* Draw the bitmap */
2455 //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
2456 MENU_DrawBitmapItem(hdc
, lpitem
, &rect
, Menu
, WndOwner
, odaction
, menuBar
);
2457 //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
2460 /* process text if present */
2466 UINT uFormat
= menuBar
?
2467 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
2468 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
2470 if (((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
))
2471 rect
.left
+= max(0, (int)(Menu
->cxTextAlign
- UserGetSystemMetrics(SM_CXMENUCHECK
)));
2473 rect
.left
+= Menu
->cxTextAlign
;
2475 if ( lpitem
->fState
& MFS_DEFAULT
)
2477 hfontOld
= NtGdiSelectFont(hdc
, ghMenuFontBold
);
2482 rect
.left
+= lpitem
->cxBmp
;
2483 if( !(lpitem
->hbmp
== HBMMENU_CALLBACK
))
2484 rect
.left
+= MenuCharSize
.cx
;
2485 rect
.right
-= MenuCharSize
.cx
;
2486 //rect.left += MENU_BAR_ITEMS_SPACE / 2;
2487 //rect.right -= MENU_BAR_ITEMS_SPACE / 2;
2490 Text
= lpitem
->Xlpstr
;
2493 for (i
= 0; L
'\0' != Text
[i
]; i
++)
2494 if (Text
[i
] == L
'\t' || Text
[i
] == L
'\b')
2498 if(lpitem
->fState
& MF_GRAYED
)
2500 if (!(lpitem
->fState
& MF_HILITE
) )
2502 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
2503 IntGdiSetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
2504 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
2505 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
2507 IntGdiSetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
2509 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
2511 /* paint the shortcut text */
2512 if (!menuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
2514 if (L
'\t' == Text
[i
])
2516 rect
.left
= lpitem
->dxTab
;
2517 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
2521 rect
.right
= lpitem
->dxTab
;
2522 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
2525 if (lpitem
->fState
& MF_GRAYED
)
2527 if (!(lpitem
->fState
& MF_HILITE
) )
2529 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
2530 IntGdiSetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
2531 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
2532 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
2534 IntGdiSetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
2536 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
2541 NtGdiSelectFont (hdc
, hfontOld
);
2546 /***********************************************************************
2549 * Paint a popup menu.
2551 static void FASTCALL
MENU_DrawPopupMenu(PWND wnd
, HDC hdc
, PMENU menu
)
2553 HBRUSH hPrevBrush
= 0, brush
= IntGetSysColorBrush(COLOR_MENU
);
2556 TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd
, hdc
, menu
);
2558 IntGetClientRect( wnd
, &rect
);
2560 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
2561 if((hPrevBrush
= NtGdiSelectBrush( hdc
, brush
))
2562 && (NtGdiSelectFont( hdc
, ghMenuFont
)))
2566 NtGdiRectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2568 hPrevPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject( NULL_PEN
) );
2571 BOOL flat_menu
= FALSE
;
2573 UserSystemParametersInfo (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2575 FrameRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_BTNSHADOW
));
2577 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
2579 TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu
), (menu
->fFlags
& MNS_STYLE_MASK
));
2580 /* draw menu items */
2581 if (menu
&& menu
->cItems
)
2586 item
= menu
->rgItems
;
2587 for( u
= menu
->cItems
; u
> 0; u
--, item
++)
2589 MENU_DrawMenuItem(wnd
, menu
, menu
->spwndNotify
, hdc
, item
,
2590 menu
->cyMenu
, FALSE
, ODA_DRAWENTIRE
);
2592 /* draw scroll arrows */
2593 if (menu
->dwArrowsOn
)
2595 MENU_DrawScrollArrows(menu
, hdc
);
2601 NtGdiSelectBrush( hdc
, hPrevBrush
);
2606 /**********************************************************************
2609 PWND
MENU_IsMenuActive(VOID
)
2611 return ValidateHwndNoErr(top_popup
);
2614 /**********************************************************************
2617 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2619 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2621 void MENU_EndMenu( PWND pwnd
)
2624 menu
= UserGetMenuObject(top_popup_hmenu
);
2625 if ( menu
&& ( UserHMGetHandle(pwnd
) == menu
->hWnd
|| pwnd
== menu
->spwndNotify
) )
2627 if (fInsideMenuLoop
&& top_popup
)
2629 fInsideMenuLoop
= FALSE
;
2633 ERR("Already in End loop\n");
2638 UserPostMessage( top_popup
, WM_CANCELMODE
, 0, 0);
2644 IntDrawMenuBarTemp(PWND pWnd
, HDC hDC
, LPRECT Rect
, PMENU pMenu
, HFONT Font
)
2647 HFONT FontOld
= NULL
;
2648 BOOL flat_menu
= FALSE
;
2650 UserSystemParametersInfo(SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2654 pMenu
= UserGetMenuObject(UlongToHandle(pWnd
->IDMenu
));
2662 if (Rect
== NULL
|| !pMenu
)
2664 return UserGetSystemMetrics(SM_CYMENU
);
2667 TRACE("(%x, %x, %p, %x, %x)\n", pWnd
, hDC
, Rect
, pMenu
, Font
);
2669 FontOld
= NtGdiSelectFont(hDC
, Font
);
2671 if (pMenu
->cyMenu
== 0)
2673 MENU_MenuBarCalcSize(hDC
, Rect
, pMenu
, pWnd
);
2676 Rect
->bottom
= Rect
->top
+ pMenu
->cyMenu
;
2678 FillRect(hDC
, Rect
, IntGetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
));
2680 NtGdiSelectPen(hDC
, NtGdiGetStockObject(DC_PEN
));
2681 IntSetDCPenColor(hDC
, IntGetSysColor(COLOR_3DFACE
));
2682 GreMoveTo(hDC
, Rect
->left
, Rect
->bottom
- 1, NULL
);
2683 NtGdiLineTo(hDC
, Rect
->right
, Rect
->bottom
- 1);
2685 if (pMenu
->cItems
== 0)
2687 NtGdiSelectFont(hDC
, FontOld
);
2688 return UserGetSystemMetrics(SM_CYMENU
);
2691 for (i
= 0; i
< pMenu
->cItems
; i
++)
2693 MENU_DrawMenuItem(pWnd
, pMenu
, pWnd
, hDC
, &pMenu
->rgItems
[i
], pMenu
->cyMenu
, TRUE
, ODA_DRAWENTIRE
);
2696 NtGdiSelectFont(hDC
, FontOld
);
2698 return pMenu
->cyMenu
;
2701 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, PWND pWnd
, BOOL suppress_draw
)
2704 PMENU lppop
= UserGetMenuObject(UlongToHandle(pWnd
->IDMenu
));
2708 // No menu. Do not reserve any space
2714 return UserGetSystemMetrics(SM_CYMENU
);
2719 hfontOld
= NtGdiSelectFont(hDC
, ghMenuFont
);
2721 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, pWnd
);
2723 lprect
->bottom
= lprect
->top
+ lppop
->cyMenu
;
2725 if (hfontOld
) NtGdiSelectFont( hDC
, hfontOld
);
2727 return lppop
->cyMenu
;
2731 return IntDrawMenuBarTemp(pWnd
, hDC
, lprect
, lppop
, NULL
);
2735 /***********************************************************************
2738 * Popup menu initialization before WM_ENTERMENULOOP.
2740 static BOOL
MENU_InitPopup( PWND pWndOwner
, PMENU menu
, UINT flags
)
2743 PPOPUPMENU pPopupMenu
;
2745 LARGE_STRING WindowName
;
2746 UNICODE_STRING ClassName
;
2747 DWORD ex_style
= WS_EX_TOOLWINDOW
;
2749 TRACE("owner=%p hmenu=%p\n", pWndOwner
, menu
);
2751 menu
->spwndNotify
= pWndOwner
;
2753 if (flags
& TPM_LAYOUTRTL
|| pWndOwner
->ExStyle
& WS_EX_LAYOUTRTL
)
2754 ex_style
= WS_EX_LAYOUTRTL
;
2756 ClassName
.Buffer
= WC_MENU
;
2757 ClassName
.Length
= 0;
2759 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2760 RtlZeroMemory(&Cs
, sizeof(Cs
));
2761 Cs
.style
= WS_POPUP
;
2762 Cs
.dwExStyle
= ex_style
;
2763 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2764 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
2765 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
2766 Cs
.lpCreateParams
= UserHMGetHandle(menu
);
2767 Cs
.hwndParent
= UserHMGetHandle(pWndOwner
);
2769 /* NOTE: In Windows, top menu popup is not owned. */
2770 pWndCreated
= co_UserCreateWindowEx( &Cs
, &ClassName
, &WindowName
, NULL
);
2772 if( !pWndCreated
) return FALSE
;
2775 // Setup pop up menu structure.
2777 menu
->hWnd
= UserHMGetHandle(pWndCreated
);
2779 pPopupMenu
= ((PMENUWND
)pWndCreated
)->ppopupmenu
;
2781 pPopupMenu
->spwndActivePopup
= pWndCreated
; // top_popup = MenuInfo.Wnd or menu->hWnd
2782 pPopupMenu
->spwndNotify
= pWndOwner
; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2783 //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2785 pPopupMenu
->fIsTrackPopup
= !!(flags
& TPM_POPUPMENU
);
2786 pPopupMenu
->fIsSysMenu
= !!(flags
& TPM_SYSTEM_MENU
);
2787 pPopupMenu
->fNoNotify
= !!(flags
& TPM_NONOTIFY
);
2788 pPopupMenu
->fRightButton
= !!(flags
& TPM_RIGHTBUTTON
);
2789 pPopupMenu
->fSynchronous
= !!(flags
& TPM_RETURNCMD
);
2791 if (pPopupMenu
->fRightButton
)
2792 pPopupMenu
->fFirstClick
= !!(UserGetKeyState(VK_RBUTTON
) & 0x8000);
2794 pPopupMenu
->fFirstClick
= !!(UserGetKeyState(VK_LBUTTON
) & 0x8000);
2796 if (gpsi
->aiSysMet
[SM_MENUDROPALIGNMENT
] ||
2797 menu
->fFlags
& MNF_RTOL
)
2799 pPopupMenu
->fDroppedLeft
= TRUE
;
2804 /***********************************************************************
2807 * Display a popup menu.
2809 static BOOL FASTCALL
MENU_ShowPopup(PWND pwndOwner
, PMENU menu
, UINT id
, UINT flags
,
2810 INT x
, INT y
, INT xanchor
, INT yanchor
)
2816 USER_REFERENCE_ENTRY Ref
;
2818 TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
2819 pwndOwner
, menu
, id
, x
, y
, xanchor
, yanchor
);
2821 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2823 menu
->rgItems
[menu
->iItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2824 menu
->iItem
= NO_SELECTED_ITEM
;
2827 menu
->dwArrowsOn
= 0;
2828 MENU_PopupMenuCalcSize(menu
, pwndOwner
);
2830 /* adjust popup menu pos so that it fits within the desktop */
2832 width
= menu
->cxMenu
+ UserGetSystemMetrics(SM_CXBORDER
);
2833 height
= menu
->cyMenu
+ UserGetSystemMetrics(SM_CYBORDER
);
2835 /* FIXME: should use item rect */
2838 monitor
= UserMonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
2840 if (flags
& TPM_LAYOUTRTL
)
2841 flags
^= TPM_RIGHTALIGN
;
2843 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
2844 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
2846 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
2847 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
2849 if( x
+ width
> monitor
->rcMonitor
.right
)
2851 if( xanchor
&& x
>= width
- xanchor
)
2852 x
-= width
- xanchor
;
2854 if( x
+ width
> monitor
->rcMonitor
.right
)
2855 x
= monitor
->rcMonitor
.right
- width
;
2857 if( x
< monitor
->rcMonitor
.left
) x
= monitor
->rcMonitor
.left
;
2859 if( y
+ height
> monitor
->rcMonitor
.bottom
)
2861 if( yanchor
&& y
>= height
+ yanchor
)
2862 y
-= height
+ yanchor
;
2864 if( y
+ height
> monitor
->rcMonitor
.bottom
)
2865 y
= monitor
->rcMonitor
.bottom
- height
;
2867 if( y
< monitor
->rcMonitor
.top
) y
= monitor
->rcMonitor
.top
;
2869 pWnd
= ValidateHwndNoErr( menu
->hWnd
);
2873 ERR("menu->hWnd bad hwnd %p\n",menu
->hWnd
);
2878 top_popup
= menu
->hWnd
;
2879 top_popup_hmenu
= UserHMGetHandle(menu
);
2882 /* Display the window */
2883 UserRefObjectCo(pWnd
, &Ref
);
2884 co_WinPosSetWindowPos( pWnd
, HWND_TOPMOST
, x
, y
, width
, height
, SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
2886 co_IntUpdateWindows(pWnd
, RDW_ALLCHILDREN
, FALSE
);
2888 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART
, pWnd
, OBJID_CLIENT
, CHILDID_SELF
, 0);
2889 UserDerefObjectCo(pWnd
);
2894 /***********************************************************************
2895 * MENU_EnsureMenuItemVisible
2897 void MENU_EnsureMenuItemVisible(PMENU lppop
, UINT wIndex
, HDC hdc
)
2899 USER_REFERENCE_ENTRY Ref
;
2900 if (lppop
->dwArrowsOn
)
2902 ITEM
*item
= &lppop
->rgItems
[wIndex
];
2903 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
2904 UINT nOldPos
= lppop
->iTop
;
2906 UINT arrow_bitmap_height
;
2907 PWND pWnd
= ValidateHwndNoErr(lppop
->hWnd
);
2909 IntGetClientRect(pWnd
, &rc
);
2911 arrow_bitmap_height
= gpsi
->oembmi
[OBI_DNARROW
].cy
;
2913 rc
.top
+= arrow_bitmap_height
;
2914 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
2916 nMaxHeight
-= UserGetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
2917 UserRefObjectCo(pWnd
, &Ref
);
2918 if (item
->cyItem
> lppop
->iTop
+ nMaxHeight
)
2920 lppop
->iTop
= item
->cyItem
- nMaxHeight
;
2921 IntScrollWindow(pWnd
, 0, nOldPos
- lppop
->iTop
, &rc
, &rc
);
2922 MENU_DrawScrollArrows(lppop
, hdc
);
2923 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2925 else if (item
->yItem
- MENU_TOP_MARGIN
< lppop
->iTop
)
2927 lppop
->iTop
= item
->yItem
- MENU_TOP_MARGIN
;
2928 IntScrollWindow(pWnd
, 0, nOldPos
- lppop
->iTop
, &rc
, &rc
);
2929 MENU_DrawScrollArrows(lppop
, hdc
);
2930 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2932 UserDerefObjectCo(pWnd
);
2936 /***********************************************************************
2939 static void FASTCALL
MENU_SelectItem(PWND pwndOwner
, PMENU menu
, UINT wIndex
,
2940 BOOL sendMenuSelect
, PMENU topmenu
)
2945 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner
, menu
, wIndex
, sendMenuSelect
);
2947 if (!menu
|| !menu
->cItems
) return;
2949 pWnd
= ValidateHwndNoErr(menu
->hWnd
);
2953 if (menu
->iItem
== wIndex
) return;
2955 if (menu
->fFlags
& MNF_POPUP
)
2956 hdc
= UserGetDCEx(pWnd
, 0, DCX_USESTYLE
);
2958 hdc
= UserGetDCEx(pWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2961 top_popup
= menu
->hWnd
; //pPopupMenu->spwndActivePopup or
2962 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
2963 top_popup_hmenu
= UserHMGetHandle(menu
); //pPopupMenu->spmenu
2966 NtGdiSelectFont( hdc
, ghMenuFont
);
2968 /* Clear previous highlighted item */
2969 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2971 menu
->rgItems
[menu
->iItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2972 MENU_DrawMenuItem(pWnd
, menu
, pwndOwner
, hdc
, &menu
->rgItems
[menu
->iItem
],
2973 menu
->cyMenu
, !(menu
->fFlags
& MNF_POPUP
),
2977 /* Highlight new item (if any) */
2978 menu
->iItem
= wIndex
;
2979 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2981 if (!(menu
->rgItems
[wIndex
].fType
& MF_SEPARATOR
))
2983 menu
->rgItems
[wIndex
].fState
|= MF_HILITE
;
2984 MENU_EnsureMenuItemVisible(menu
, wIndex
, hdc
);
2985 MENU_DrawMenuItem(pWnd
, menu
, pwndOwner
, hdc
,
2986 &menu
->rgItems
[wIndex
], menu
->cyMenu
, !(menu
->fFlags
& MNF_POPUP
), ODA_SELECT
);
2990 ITEM
*ip
= &menu
->rgItems
[menu
->iItem
];
2991 WPARAM wParam
= MAKEWPARAM( ip
->spSubMenu
? wIndex
: ip
->wID
,
2992 ip
->fType
| ip
->fState
|
2993 (ip
->spSubMenu
? MF_POPUP
: 0) |
2994 (menu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0 ) );
2996 co_IntSendMessage(UserHMGetHandle(pwndOwner
), WM_MENUSELECT
, wParam
, (LPARAM
) UserHMGetHandle(menu
));
2999 else if (sendMenuSelect
)
3004 pos
= MENU_FindSubMenu(&topmenu
, menu
);
3005 if (pos
!= NO_SELECTED_ITEM
)
3007 ITEM
*ip
= &topmenu
->rgItems
[pos
];
3008 WPARAM wParam
= MAKEWPARAM( Pos
, ip
->fType
| ip
->fState
|
3009 (ip
->spSubMenu
? MF_POPUP
: 0) |
3010 (topmenu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0 ) );
3012 co_IntSendMessage(UserHMGetHandle(pwndOwner
), WM_MENUSELECT
, wParam
, (LPARAM
) UserHMGetHandle(topmenu
));
3016 UserReleaseDC(pWnd
, hdc
, FALSE
);
3019 /***********************************************************************
3022 * Moves currently selected item according to the Offset parameter.
3023 * If there is no selection then it should select the last item if
3024 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3026 static void FASTCALL
MENU_MoveSelection(PWND pwndOwner
, PMENU menu
, INT offset
)
3030 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner
, menu
, offset
);
3032 if ((!menu
) || (!menu
->rgItems
)) return;
3034 if ( menu
->iItem
!= NO_SELECTED_ITEM
)
3036 if ( menu
->cItems
== 1 )
3039 for (i
= menu
->iItem
+ offset
; i
>= 0 && i
< menu
->cItems
3041 if (!(menu
->rgItems
[i
].fType
& MF_SEPARATOR
))
3043 MENU_SelectItem( pwndOwner
, menu
, i
, TRUE
, 0 );
3048 for ( i
= (offset
> 0) ? 0 : menu
->cItems
- 1;
3049 i
>= 0 && i
< menu
->cItems
; i
+= offset
)
3050 if (!(menu
->rgItems
[i
].fType
& MF_SEPARATOR
))
3052 MENU_SelectItem( pwndOwner
, menu
, i
, TRUE
, 0 );
3057 /***********************************************************************
3060 * Hide the sub-popup menus of this menu.
3062 static void FASTCALL
MENU_HideSubPopups(PWND pWndOwner
, PMENU Menu
,
3063 BOOL SendMenuSelect
, UINT wFlags
)
3065 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner
, Menu
, SendMenuSelect
);
3067 if ( Menu
&& top_popup
)
3071 if (Menu
->iItem
!= NO_SELECTED_ITEM
)
3073 Item
= &Menu
->rgItems
[Menu
->iItem
];
3074 if (!(Item
->spSubMenu
) ||
3075 !(Item
->fState
& MF_MOUSESELECT
)) return;
3076 Item
->fState
&= ~MF_MOUSESELECT
;
3081 if (Item
->spSubMenu
)
3084 if (!VerifyMenu(Item
->spSubMenu
)) return;
3085 pWnd
= ValidateHwndNoErr(Item
->spSubMenu
->hWnd
);
3086 MENU_HideSubPopups(pWndOwner
, Item
->spSubMenu
, FALSE
, wFlags
);
3087 MENU_SelectItem(pWndOwner
, Item
->spSubMenu
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
3088 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu
,pWndOwner
->IDMenu
);
3089 co_UserDestroyWindow(pWnd
);
3091 /* Native returns handle to destroyed window */
3092 if (!(wFlags
& TPM_NONOTIFY
))
3094 co_IntSendMessage( UserHMGetHandle(pWndOwner
), WM_UNINITMENUPOPUP
, (WPARAM
)UserHMGetHandle(Item
->spSubMenu
),
3095 MAKELPARAM(0, IS_SYSTEM_MENU(Item
->spSubMenu
)) );
3098 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3099 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3101 Item
->spSubMenu
->hWnd
= NULL
;
3107 /***********************************************************************
3110 * Display the sub-menu of the selected item of this menu.
3111 * Return the handle of the submenu, or menu if no submenu to display.
3113 static PMENU FASTCALL
MENU_ShowSubPopup(PWND WndOwner
, PMENU Menu
, BOOL SelectFirst
, UINT Flags
)
3120 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner
, Menu
, SelectFirst
);
3122 if (!Menu
) return Menu
;
3124 if (Menu
->iItem
== NO_SELECTED_ITEM
) return Menu
;
3126 Item
= &Menu
->rgItems