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 (!((MenuInfo)->fFlags & MNF_POPUP) && ((MenuInfo)->fFlags & MNF_SYSMENU))
61 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
63 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
66 /* Maximum number of menu items a menu can contain */
67 #define MAX_MENU_ITEMS (0x4000)
68 #define MAX_GOINTOSUBMENU (0x10)
70 /* Space between 2 columns */
71 #define MENU_COL_SPACE 4
73 /* top and bottom margins for popup menus */
74 #define MENU_TOP_MARGIN 2 //3
75 #define MENU_BOTTOM_MARGIN 2
77 #define MENU_ITEM_HBMP_SPACE (5)
78 #define MENU_BAR_ITEMS_SPACE (12)
79 #define SEPARATOR_HEIGHT (5)
80 #define MENU_TAB_SPACE (8)
85 PMENU CurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
86 PMENU TopMenu
; /* initial menu */
87 PWND OwnerWnd
; /* where notifications are sent */
91 /* Internal MenuTrackMenu() flags */
92 #define TPM_INTERNAL 0xF0000000
93 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
94 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
99 #define UpdateMenuItemState(state, change) \
101 if((change) & MF_GRAYED) { \
102 (state) |= MF_GRAYED; \
104 (state) &= ~MF_GRAYED; \
105 } /* Separate the two for test_menu_resource_layout.*/ \
106 if((change) & MF_DISABLED) { \
107 (state) |= MF_DISABLED; \
109 (state) &= ~MF_DISABLED; \
111 if((change) & MFS_CHECKED) { \
112 (state) |= MFS_CHECKED; \
114 (state) &= ~MFS_CHECKED; \
116 if((change) & MFS_HILITE) { \
117 (state) |= MFS_HILITE; \
119 (state) &= ~MFS_HILITE; \
121 if((change) & MFS_DEFAULT) { \
122 (state) |= MFS_DEFAULT; \
124 (state) &= ~MFS_DEFAULT; \
126 if((change) & MF_MOUSESELECT) { \
127 (state) |= MF_MOUSESELECT; \
129 (state) &= ~MF_MOUSESELECT; \
135 DumpMenuItemList(PMENU Menu
, PITEM MenuItem
)
137 UINT cnt
= 0, i
= Menu
->cItems
;
140 if(MenuItem
->lpstr
.Length
)
141 DbgPrint(" %d. %wZ\n", ++cnt
, &MenuItem
->lpstr
);
143 DbgPrint(" %d. NO TEXT dwTypeData==%d\n", ++cnt
, (DWORD
)MenuItem
->lpstr
.Buffer
);
145 if(MFT_BITMAP
& MenuItem
->fType
)
146 DbgPrint("MFT_BITMAP ");
147 if(MFT_MENUBARBREAK
& MenuItem
->fType
)
148 DbgPrint("MFT_MENUBARBREAK ");
149 if(MFT_MENUBREAK
& MenuItem
->fType
)
150 DbgPrint("MFT_MENUBREAK ");
151 if(MFT_OWNERDRAW
& MenuItem
->fType
)
152 DbgPrint("MFT_OWNERDRAW ");
153 if(MFT_RADIOCHECK
& MenuItem
->fType
)
154 DbgPrint("MFT_RADIOCHECK ");
155 if(MFT_RIGHTJUSTIFY
& MenuItem
->fType
)
156 DbgPrint("MFT_RIGHTJUSTIFY ");
157 if(MFT_SEPARATOR
& MenuItem
->fType
)
158 DbgPrint("MFT_SEPARATOR ");
159 if(MFT_STRING
& MenuItem
->fType
)
160 DbgPrint("MFT_STRING ");
161 DbgPrint("\n fState=");
162 if(MFS_DISABLED
& MenuItem
->fState
)
163 DbgPrint("MFS_DISABLED ");
165 DbgPrint("MFS_ENABLED ");
166 if(MFS_CHECKED
& MenuItem
->fState
)
167 DbgPrint("MFS_CHECKED ");
169 DbgPrint("MFS_UNCHECKED ");
170 if(MFS_HILITE
& MenuItem
->fState
)
171 DbgPrint("MFS_HILITE ");
173 DbgPrint("MFS_UNHILITE ");
174 if(MFS_DEFAULT
& MenuItem
->fState
)
175 DbgPrint("MFS_DEFAULT ");
176 if(MFS_GRAYED
& MenuItem
->fState
)
177 DbgPrint("MFS_GRAYED ");
178 DbgPrint("\n wId=%d\n", MenuItem
->wID
);
182 DbgPrint("Entries: %d\n", cnt
);
187 #define FreeMenuText(Menu,MenuItem) \
189 if((MENU_ITEM_TYPE((MenuItem)->fType) == MF_STRING) && \
190 (MenuItem)->lpstr.Length) { \
191 DesktopHeapFree(((PMENU)Menu)->head.rpdesk, (MenuItem)->lpstr.Buffer); \
196 IntGetMenuObject(HMENU hMenu
)
198 PMENU Menu
= UserGetMenuObject(hMenu
);
200 Menu
->head
.cLockObj
++;
205 PMENU FASTCALL
VerifyMenu(PMENU pMenu
)
211 if (!pMenu
) return NULL
;
213 Error
= EngGetLastError();
217 hMenu
= UserHMGetHandle(pMenu
);
218 pItem
= pMenu
->rgItems
;
225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
227 ERR("Run away LOOP!\n");
228 EngSetLastError(Error
);
229 _SEH2_YIELD(return NULL
);
233 if ( UserObjectInDestroy(hMenu
))
235 ERR("Menu is marked for destruction!\n");
238 EngSetLastError(Error
);
244 IntIsMenu(HMENU Menu
)
246 if (UserGetMenuObject(Menu
)) return TRUE
;
252 IntGetMenu(HWND hWnd
)
254 PWND Wnd
= ValidateHwndNoErr(hWnd
);
259 return UserGetMenuObject(UlongToHandle(Wnd
->IDMenu
));
262 PMENU
get_win_sys_menu( HWND hwnd
)
265 WND
*win
= ValidateHwndNoErr( hwnd
);
268 ret
= UserGetMenuObject(win
->SystemMenu
);
273 BOOL
IntDestroyMenu( PMENU pMenu
, BOOL bRecurse
)
277 if (pMenu
->rgItems
) /* recursively destroy submenus */
280 ITEM
*item
= pMenu
->rgItems
;
281 for (i
= pMenu
->cItems
; i
> 0; i
--, item
++)
283 SubMenu
= item
->spSubMenu
;
284 item
->spSubMenu
= NULL
;
286 /* Remove Item Text */
287 FreeMenuText(pMenu
,item
);
289 /* Remove Item Bitmap and set it for this process */
290 if (item
->hbmp
&& !(item
->fState
& MFS_HBMMENUBMP
))
292 GreSetObjectOwner(item
->hbmp
, GDI_OBJ_HMGR_POWNED
);
296 /* Remove Item submenu */
297 if (bRecurse
&& SubMenu
)//VerifyMenu(SubMenu))
299 /* Release submenu since it was referenced when inserted */
300 IntReleaseMenuObject(SubMenu
);
301 IntDestroyMenuObject(SubMenu
, bRecurse
);
305 DesktopHeapFree(pMenu
->head
.rpdesk
, pMenu
->rgItems
);
306 pMenu
->rgItems
= NULL
;
312 /* Callback for the object manager */
314 UserDestroyMenuObject(PVOID Object
)
316 return IntDestroyMenuObject(Object
, TRUE
);
320 IntDestroyMenuObject(PMENU Menu
, BOOL bRecurse
)
326 if (PsGetCurrentProcessSessionId() == Menu
->head
.rpdesk
->rpwinstaParent
->dwSessionId
)
331 Window
= ValidateHwndNoErr(Menu
->hWnd
);
334 //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
336 /* DestroyMenu should not destroy system menu popup owner */
337 if ((Menu
->fFlags
& (MNF_POPUP
| MNF_SYSSUBMENU
)) == MNF_POPUP
)
339 // Should we check it to see if it has Class?
340 ERR("FIXME Pop up menu window thing'ie\n");
341 //co_UserDestroyWindow( Window );
347 if (!UserMarkObjectDestroy(Menu
)) return TRUE
;
349 /* Remove all menu items */
350 IntDestroyMenu( Menu
, bRecurse
);
352 ret
= UserDeleteObject(Menu
->head
.h
, TYPE_MENU
);
353 TRACE("IntDestroyMenuObject %d\n",ret
);
363 NONCLIENTMETRICSW ncm
;
365 /* get the menu font */
366 if (!ghMenuFont
|| !ghMenuFontBold
)
368 ncm
.cbSize
= sizeof(ncm
);
369 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
371 ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
375 ghMenuFont
= GreCreateFontIndirectW(&ncm
.lfMenuFont
);
376 if (ghMenuFont
== NULL
)
378 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
381 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
382 ghMenuFontBold
= GreCreateFontIndirectW(&ncm
.lfMenuFont
);
383 if (ghMenuFontBold
== NULL
)
385 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
386 GreDeleteObject(ghMenuFont
);
391 GreSetObjectOwner(ghMenuFont
, GDI_OBJ_HMGR_PUBLIC
);
392 GreSetObjectOwner(ghMenuFontBold
, GDI_OBJ_HMGR_PUBLIC
);
401 /**********************************************************************
404 * detect if there are loops in the menu tree (or the depth is too large)
406 int FASTCALL
MENU_depth( PMENU pmenu
, int depth
)
412 if (!pmenu
) return depth
;
415 if( depth
> MAXMENUDEPTH
) return depth
;
416 item
= pmenu
->rgItems
;
418 for( i
= 0; i
< pmenu
->cItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++)
420 if( item
->spSubMenu
)//VerifyMenu(item->spSubMenu))
422 int bdepth
= MENU_depth( item
->spSubMenu
, depth
);
423 if( bdepth
> subdepth
) subdepth
= bdepth
;
425 if( subdepth
> MAXMENUDEPTH
)
426 TRACE("<- hmenu %p\n", item
->spSubMenu
);
432 /******************************************************************************
434 * UINT MenuGetStartOfNextColumn(
437 *****************************************************************************/
439 static UINT
MENU_GetStartOfNextColumn(
446 return NO_SELECTED_ITEM
;
449 if( i
== NO_SELECTED_ITEM
)
452 pItem
= menu
->rgItems
;
453 if (!pItem
) return NO_SELECTED_ITEM
;
455 for( ; i
< menu
->cItems
; ++i
) {
456 if (pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
460 return NO_SELECTED_ITEM
;
463 /******************************************************************************
465 * UINT MenuGetStartOfPrevColumn(
468 *****************************************************************************/
469 static UINT
MENU_GetStartOfPrevColumn(
476 return NO_SELECTED_ITEM
;
478 if( menu
->iItem
== 0 || menu
->iItem
== NO_SELECTED_ITEM
)
479 return NO_SELECTED_ITEM
;
481 pItem
= menu
->rgItems
;
482 if (!pItem
) return NO_SELECTED_ITEM
;
484 /* Find the start of the column */
486 for(i
= menu
->iItem
; i
!= 0 &&
487 !(pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
491 return NO_SELECTED_ITEM
;
493 for(--i
; i
!= 0; --i
) {
494 if (pItem
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
498 TRACE("ret %d.\n", i
);
503 /***********************************************************************
506 * Find a menu item. Return a pointer on the item, and modifies *hmenu
507 * in case the item was in a sub-menu.
509 PITEM FASTCALL
MENU_FindItem( PMENU
*pmenu
, UINT
*nPos
, UINT wFlags
)
512 ITEM
*fallback
= NULL
;
513 UINT fallback_pos
= 0;
516 if (!menu
) return NULL
;
518 if (wFlags
& MF_BYPOSITION
)
520 if (!menu
->cItems
) return NULL
;
521 if (*nPos
>= menu
->cItems
) return NULL
;
522 return &menu
->rgItems
[*nPos
];
526 PITEM item
= menu
->rgItems
;
527 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
531 PMENU psubmenu
= item
->spSubMenu
;//VerifyMenu(item->spSubMenu);
532 PITEM subitem
= MENU_FindItem( &psubmenu
, nPos
, wFlags
);
538 else if (item
->wID
== *nPos
)
540 /* fallback to this item if nothing else found */
545 else if (item
->wID
== *nPos
)
554 *nPos
= fallback_pos
;
559 /***********************************************************************
562 * Find a Sub menu. Return the position of the submenu, and modifies
563 * *hmenu in case it is found in another sub-menu.
564 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
566 static UINT FASTCALL
MENU_FindSubMenu(PMENU
*menu
, PMENU SubTarget
)
571 item
= ((PMENU
)*menu
)->rgItems
;
572 for (i
= 0; i
< ((PMENU
)*menu
)->cItems
; i
++, item
++)
574 if (!item
->spSubMenu
)
578 if (item
->spSubMenu
== SubTarget
)
584 PMENU pSubMenu
= item
->spSubMenu
;
585 UINT pos
= MENU_FindSubMenu( &pSubMenu
, SubTarget
);
586 if (pos
!= NO_SELECTED_ITEM
)
594 return NO_SELECTED_ITEM
;
598 IntRemoveMenuItem( PMENU pMenu
, UINT nPos
, UINT wFlags
, BOOL bRecurse
)
602 TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu
, nPos
, wFlags
);
603 if (!(item
= MENU_FindItem( &pMenu
, &nPos
, wFlags
))) return FALSE
;
607 FreeMenuText(pMenu
,item
);
608 if (bRecurse
&& item
->spSubMenu
)
610 IntDestroyMenuObject(item
->spSubMenu
, bRecurse
);
612 ////// Use cAlloced with inc's of 8's....
613 if (--pMenu
->cItems
== 0)
615 DesktopHeapFree(pMenu
->head
.rpdesk
, pMenu
->rgItems
);
616 pMenu
->rgItems
= NULL
;
620 while(nPos
< pMenu
->cItems
)
626 pMenu
->rgItems
= DesktopHeapReAlloc(pMenu
->head
.rpdesk
, pMenu
->rgItems
, pMenu
->cItems
* sizeof(ITEM
));
631 /**********************************************************************
634 * Insert (allocate) a new item into a menu.
636 ITEM
*MENU_InsertItem( PMENU menu
, UINT pos
, UINT flags
, PMENU
*submenu
, UINT
*npos
)
640 /* Find where to insert new item */
642 if (flags
& MF_BYPOSITION
) {
643 if (pos
> menu
->cItems
)
646 if (!MENU_FindItem( &menu
, &pos
, flags
))
648 if (submenu
) *submenu
= menu
;
649 if (npos
) *npos
= pos
;
654 /* Make sure that MDI system buttons stay on the right side.
655 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
656 * regardless of their id.
659 (INT_PTR
)menu
->rgItems
[pos
- 1].hbmp
>= (INT_PTR
)HBMMENU_SYSTEM
&&
660 (INT_PTR
)menu
->rgItems
[pos
- 1].hbmp
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
663 TRACE("inserting at %u flags %x\n", pos
, flags
);
665 /* Create new items array */
667 newItems
= DesktopHeapAlloc(menu
->head
.rpdesk
, sizeof(ITEM
) * (menu
->cItems
+1) );
670 WARN("allocation failed\n" );
673 if (menu
->cItems
> 0)
675 /* Copy the old array into the new one */
676 if (pos
> 0) RtlCopyMemory( newItems
, menu
->rgItems
, pos
* sizeof(ITEM
) );
677 if (pos
< menu
->cItems
) RtlCopyMemory( &newItems
[pos
+1], &menu
->rgItems
[pos
], (menu
->cItems
-pos
)*sizeof(ITEM
) );
678 DesktopHeapFree(menu
->head
.rpdesk
, menu
->rgItems
);
680 menu
->rgItems
= newItems
;
682 RtlZeroMemory( &newItems
[pos
], sizeof(*newItems
) );
683 menu
->cyMenu
= 0; /* force size recalculate */
684 return &newItems
[pos
];
689 _In_ PMENU MenuObject
,
692 PROSMENUITEMINFO ItemInfo
,
693 PUNICODE_STRING lpstr
)
696 PMENU SubMenu
= NULL
;
698 NT_ASSERT(MenuObject
!= NULL
);
700 if (MAX_MENU_ITEMS
<= MenuObject
->cItems
)
702 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
706 SubMenu
= MenuObject
;
708 if(!(MenuItem
= MENU_InsertItem( SubMenu
, uItem
, fByPosition
? MF_BYPOSITION
: MF_BYCOMMAND
, &SubMenu
, &uItem
))) return FALSE
;
710 if(!IntSetMenuItemInfo(SubMenu
, MenuItem
, ItemInfo
, lpstr
))
712 IntRemoveMenuItem(SubMenu
, uItem
, fByPosition
? MF_BYPOSITION
: MF_BYCOMMAND
, FALSE
);
716 /* Force size recalculation! */
718 MenuItem
->hbmpChecked
= MenuItem
->hbmpUnchecked
= 0;
720 TRACE("IntInsertMenuItemToList = %u %i\n", uItem
, (BOOL
)((INT
)uItem
>= 0));
727 _Out_ PHANDLE Handle
,
729 _In_ PDESKTOP Desktop
,
730 _In_ PPROCESSINFO ppi
)
734 Menu
= (PMENU
)UserCreateObject( gHandleTable
,
746 Menu
->cyMax
= 0; /* Default */
747 Menu
->hbrBack
= NULL
; /* No brush */
748 Menu
->dwContextHelpId
= 0; /* Default */
749 Menu
->dwMenuData
= 0; /* Default */
750 Menu
->iItem
= NO_SELECTED_ITEM
; // Focused item
751 Menu
->fFlags
= (IsMenuBar
? 0 : MNF_POPUP
);
752 Menu
->spwndNotify
= NULL
;
753 Menu
->cyMenu
= 0; // Height
754 Menu
->cxMenu
= 0; // Width
755 Menu
->cItems
= 0; // Item count
758 Menu
->cxTextAlign
= 0;
759 Menu
->rgItems
= NULL
;
762 Menu
->TimeToHide
= FALSE
;
768 IntCloneMenuItems(PMENU Destination
, PMENU Source
)
770 PITEM MenuItem
, NewMenuItem
= NULL
;
776 NewMenuItem
= DesktopHeapAlloc(Destination
->head
.rpdesk
, (Source
->cItems
+1) * sizeof(ITEM
));
777 if(!NewMenuItem
) return FALSE
;
779 RtlZeroMemory(NewMenuItem
, (Source
->cItems
+1) * sizeof(ITEM
));
781 Destination
->rgItems
= NewMenuItem
;
783 MenuItem
= Source
->rgItems
;
784 for (i
= 0; i
< Source
->cItems
; i
++, MenuItem
++, NewMenuItem
++)
786 NewMenuItem
->fType
= MenuItem
->fType
;
787 NewMenuItem
->fState
= MenuItem
->fState
;
788 NewMenuItem
->wID
= MenuItem
->wID
;
789 NewMenuItem
->spSubMenu
= MenuItem
->spSubMenu
;
790 NewMenuItem
->hbmpChecked
= MenuItem
->hbmpChecked
;
791 NewMenuItem
->hbmpUnchecked
= MenuItem
->hbmpUnchecked
;
792 NewMenuItem
->dwItemData
= MenuItem
->dwItemData
;
793 if (MenuItem
->lpstr
.Length
)
795 NewMenuItem
->lpstr
.Length
= 0;
796 NewMenuItem
->lpstr
.MaximumLength
= MenuItem
->lpstr
.MaximumLength
;
797 NewMenuItem
->lpstr
.Buffer
= DesktopHeapAlloc(Destination
->head
.rpdesk
, MenuItem
->lpstr
.MaximumLength
);
798 if (!NewMenuItem
->lpstr
.Buffer
)
800 DesktopHeapFree(Destination
->head
.rpdesk
, NewMenuItem
);
803 RtlCopyUnicodeString(&NewMenuItem
->lpstr
, &MenuItem
->lpstr
);
804 NewMenuItem
->lpstr
.Buffer
[MenuItem
->lpstr
.Length
/ sizeof(WCHAR
)] = 0;
805 NewMenuItem
->Xlpstr
= NewMenuItem
->lpstr
.Buffer
;
809 NewMenuItem
->lpstr
.Buffer
= MenuItem
->lpstr
.Buffer
;
810 NewMenuItem
->Xlpstr
= NewMenuItem
->lpstr
.Buffer
;
812 NewMenuItem
->hbmp
= MenuItem
->hbmp
;
818 IntCloneMenu(PMENU Source
)
826 /* A menu is valid process wide. We can pass to the object manager any thread ptr */
827 Menu
= (PMENU
)UserCreateObject( gHandleTable
,
829 ((PPROCESSINFO
)Source
->head
.hTaskWow
)->ptiList
,
836 Menu
->fFlags
= Source
->fFlags
;
837 Menu
->cyMax
= Source
->cyMax
;
838 Menu
->hbrBack
= Source
->hbrBack
;
839 Menu
->dwContextHelpId
= Source
->dwContextHelpId
;
840 Menu
->dwMenuData
= Source
->dwMenuData
;
841 Menu
->iItem
= NO_SELECTED_ITEM
;
842 Menu
->spwndNotify
= NULL
;
845 Menu
->cItems
= Source
->cItems
;
848 Menu
->cxTextAlign
= 0;
849 Menu
->rgItems
= NULL
;
852 Menu
->TimeToHide
= FALSE
;
854 IntCloneMenuItems(Menu
, Source
);
860 IntSetMenuFlagRtoL(PMENU Menu
)
862 ERR("SetMenuFlagRtoL\n");
863 Menu
->fFlags
|= MNF_RTOL
;
868 IntSetMenuContextHelpId(PMENU Menu
, DWORD dwContextHelpId
)
870 Menu
->dwContextHelpId
= dwContextHelpId
;
875 IntGetMenuInfo(PMENU Menu
, PROSMENUINFO lpmi
)
877 if(lpmi
->fMask
& MIM_BACKGROUND
)
878 lpmi
->hbrBack
= Menu
->hbrBack
;
879 if(lpmi
->fMask
& MIM_HELPID
)
880 lpmi
->dwContextHelpID
= Menu
->dwContextHelpId
;
881 if(lpmi
->fMask
& MIM_MAXHEIGHT
)
882 lpmi
->cyMax
= Menu
->cyMax
;
883 if(lpmi
->fMask
& MIM_MENUDATA
)
884 lpmi
->dwMenuData
= Menu
->dwMenuData
;
885 if(lpmi
->fMask
& MIM_STYLE
)
886 lpmi
->dwStyle
= Menu
->fFlags
& MNS_STYLE_MASK
;
888 if (sizeof(MENUINFO
) < lpmi
->cbSize
)
890 lpmi
->cItems
= Menu
->cItems
;
892 lpmi
->iItem
= Menu
->iItem
;
893 lpmi
->cxMenu
= Menu
->cxMenu
;
894 lpmi
->cyMenu
= Menu
->cyMenu
;
895 lpmi
->spwndNotify
= Menu
->spwndNotify
;
896 lpmi
->cxTextAlign
= Menu
->cxTextAlign
;
897 lpmi
->iTop
= Menu
->iTop
;
898 lpmi
->iMaxTop
= Menu
->iMaxTop
;
899 lpmi
->dwArrowsOn
= Menu
->dwArrowsOn
;
901 lpmi
->fFlags
= Menu
->fFlags
;
902 lpmi
->Self
= Menu
->head
.h
;
903 lpmi
->TimeToHide
= Menu
->TimeToHide
;
904 lpmi
->Wnd
= Menu
->hWnd
;
910 IntSetMenuInfo(PMENU Menu
, PROSMENUINFO lpmi
)
912 if(lpmi
->fMask
& MIM_BACKGROUND
)
913 Menu
->hbrBack
= lpmi
->hbrBack
;
914 if(lpmi
->fMask
& MIM_HELPID
)
915 Menu
->dwContextHelpId
= lpmi
->dwContextHelpID
;
916 if(lpmi
->fMask
& MIM_MAXHEIGHT
)
917 Menu
->cyMax
= lpmi
->cyMax
;
918 if(lpmi
->fMask
& MIM_MENUDATA
)
919 Menu
->dwMenuData
= lpmi
->dwMenuData
;
920 if(lpmi
->fMask
& MIM_STYLE
)
921 Menu
->fFlags
^= (Menu
->fFlags
^ lpmi
->dwStyle
) & MNS_STYLE_MASK
;
922 if(lpmi
->fMask
& MIM_APPLYTOSUBMENUS
)
925 PITEM item
= Menu
->rgItems
;
926 for ( i
= Menu
->cItems
; i
; i
--, item
++)
928 if ( item
->spSubMenu
)
930 IntSetMenuInfo( item
->spSubMenu
, lpmi
);
934 if (sizeof(MENUINFO
) < lpmi
->cbSize
)
936 Menu
->iItem
= lpmi
->iItem
;
937 Menu
->cyMenu
= lpmi
->cyMenu
;
938 Menu
->cxMenu
= lpmi
->cxMenu
;
939 Menu
->spwndNotify
= lpmi
->spwndNotify
;
940 Menu
->cxTextAlign
= lpmi
->cxTextAlign
;
941 Menu
->iTop
= lpmi
->iTop
;
942 Menu
->iMaxTop
= lpmi
->iMaxTop
;
943 Menu
->dwArrowsOn
= lpmi
->dwArrowsOn
;
945 Menu
->TimeToHide
= lpmi
->TimeToHide
;
946 Menu
->hWnd
= lpmi
->Wnd
;
948 if ( lpmi
->fMask
& MIM_STYLE
)
950 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented wine\n");
951 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented wine\n");
952 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented wine\n");
958 IntGetMenuItemInfo(PMENU Menu
, /* UNUSED PARAM!! */
959 PITEM MenuItem
, PROSMENUITEMINFO lpmii
)
963 if(lpmii
->fMask
& (MIIM_FTYPE
| MIIM_TYPE
))
965 lpmii
->fType
= MenuItem
->fType
;
967 if(lpmii
->fMask
& MIIM_BITMAP
)
969 lpmii
->hbmpItem
= MenuItem
->hbmp
;
971 if(lpmii
->fMask
& MIIM_CHECKMARKS
)
973 lpmii
->hbmpChecked
= MenuItem
->hbmpChecked
;
974 lpmii
->hbmpUnchecked
= MenuItem
->hbmpUnchecked
;
976 if(lpmii
->fMask
& MIIM_DATA
)
978 lpmii
->dwItemData
= MenuItem
->dwItemData
;
980 if(lpmii
->fMask
& MIIM_ID
)
982 lpmii
->wID
= MenuItem
->wID
;
984 if(lpmii
->fMask
& MIIM_STATE
)
986 lpmii
->fState
= MenuItem
->fState
;
988 if(lpmii
->fMask
& MIIM_SUBMENU
)
990 lpmii
->hSubMenu
= MenuItem
->spSubMenu
? MenuItem
->spSubMenu
->head
.h
: NULL
;
993 if ((lpmii
->fMask
& MIIM_STRING
) ||
994 ((lpmii
->fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(lpmii
->fType
) == MF_STRING
)))
996 if (lpmii
->dwTypeData
== NULL
)
998 lpmii
->cch
= MenuItem
->lpstr
.Length
/ sizeof(WCHAR
);
1001 { //// lpmii->lpstr can be read in user mode!!!!
1002 Status
= MmCopyToCaller(lpmii
->dwTypeData
, MenuItem
->lpstr
.Buffer
,
1003 min(lpmii
->cch
* sizeof(WCHAR
),
1004 MenuItem
->lpstr
.MaximumLength
));
1005 if (! NT_SUCCESS(Status
))
1007 SetLastNtError(Status
);
1013 if (sizeof(ROSMENUITEMINFO
) == lpmii
->cbSize
)
1015 lpmii
->Rect
.left
= MenuItem
->xItem
;
1016 lpmii
->Rect
.top
= MenuItem
->yItem
;
1017 lpmii
->Rect
.right
= MenuItem
->cxItem
; // Do this for now......
1018 lpmii
->Rect
.bottom
= MenuItem
->cyItem
;
1019 lpmii
->dxTab
= MenuItem
->dxTab
;
1020 lpmii
->lpstr
= MenuItem
->lpstr
.Buffer
;
1021 lpmii
->maxBmpSize
.cx
= MenuItem
->cxBmp
;
1022 lpmii
->maxBmpSize
.cy
= MenuItem
->cyBmp
;
1029 IntSetMenuItemInfo(PMENU MenuObject
, PITEM MenuItem
, PROSMENUITEMINFO lpmii
, PUNICODE_STRING lpstr
)
1031 PMENU SubMenuObject
;
1032 BOOL circref
= FALSE
;
1034 if(!MenuItem
|| !MenuObject
|| !lpmii
)
1038 if ( lpmii
->fMask
& MIIM_FTYPE
)
1040 MenuItem
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
1041 MenuItem
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
1043 if (lpmii
->fMask
& MIIM_TYPE
)
1045 #if 0 //// Done in User32.
1046 if (lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
))
1048 ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1049 KeRosDumpStackFrames(NULL
, 20);
1050 /* This does not happen on Win9x/ME */
1051 SetLastNtError( ERROR_INVALID_PARAMETER
);
1056 * Delete the menu item type when changing type from
1059 if (MenuItem
->fType
!= lpmii
->fType
&&
1060 MENU_ITEM_TYPE(MenuItem
->fType
) == MFT_STRING
)
1062 FreeMenuText(MenuObject
,MenuItem
);
1063 RtlInitUnicodeString(&MenuItem
->lpstr
, NULL
);
1064 MenuItem
->Xlpstr
= NULL
;
1066 if(lpmii
->fType
& MFT_BITMAP
)
1069 MenuItem
->hbmp
= lpmii
->hbmpItem
;
1071 { /* Win 9x/Me stuff */
1072 MenuItem
->hbmp
= (HBITMAP
)((ULONG_PTR
)(LOWORD(lpmii
->dwTypeData
)));
1074 lpmii
->dwTypeData
= 0;
1077 if(lpmii
->fMask
& MIIM_BITMAP
)
1079 MenuItem
->hbmp
= lpmii
->hbmpItem
;
1080 if (MenuItem
->hbmp
<= HBMMENU_POPUP_MINIMIZE
&& MenuItem
->hbmp
>= HBMMENU_CALLBACK
)
1081 MenuItem
->fState
|= MFS_HBMMENUBMP
;
1083 MenuItem
->fState
&= ~MFS_HBMMENUBMP
;
1085 if(lpmii
->fMask
& MIIM_CHECKMARKS
)
1087 MenuItem
->hbmpChecked
= lpmii
->hbmpChecked
;
1088 MenuItem
->hbmpUnchecked
= lpmii
->hbmpUnchecked
;
1090 if(lpmii
->fMask
& MIIM_DATA
)
1092 MenuItem
->dwItemData
= lpmii
->dwItemData
;
1094 if(lpmii
->fMask
& MIIM_ID
)
1096 MenuItem
->wID
= lpmii
->wID
;
1098 if(lpmii
->fMask
& MIIM_STATE
)
1100 /* Remove MFS_DEFAULT flag from all other menu items if this item
1101 has the MFS_DEFAULT state */
1102 if(lpmii
->fState
& MFS_DEFAULT
)
1103 UserSetMenuDefaultItem(MenuObject
, -1, 0);
1104 /* Update the menu item state flags */
1105 UpdateMenuItemState(MenuItem
->fState
, lpmii
->fState
);
1108 if(lpmii
->fMask
& MIIM_SUBMENU
)
1110 if (lpmii
->hSubMenu
)
1112 SubMenuObject
= UserGetMenuObject(lpmii
->hSubMenu
);
1113 if ( SubMenuObject
&& !(UserObjectInDestroy(lpmii
->hSubMenu
)) )
1115 //// wine Bug 12171 : Adding Popup Menu to itself! Could create endless loops.
1117 if (MenuObject
== SubMenuObject
)
1120 ERR("Pop Up Menu Double Trouble!\n");
1121 SubMenuObject
= IntCreateMenu(&hMenu
,
1123 MenuObject
->head
.rpdesk
,
1124 (PPROCESSINFO
)MenuObject
->head
.hTaskWow
); // It will be marked.
1125 if (!SubMenuObject
) return FALSE
;
1126 IntReleaseMenuObject(SubMenuObject
); // This will be referenced again after insertion.
1129 if ( MENU_depth( SubMenuObject
, 0) > MAXMENUDEPTH
)
1131 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1132 if (circref
) IntDestroyMenuObject(SubMenuObject
, FALSE
);
1135 /* Make sure the submenu is marked as a popup menu */
1136 SubMenuObject
->fFlags
|= MNF_POPUP
;
1137 // Now fix the test_subpopup_locked_by_menu tests....
1138 if (MenuItem
->spSubMenu
) IntReleaseMenuObject(MenuItem
->spSubMenu
);
1139 MenuItem
->spSubMenu
= SubMenuObject
;
1140 UserReferenceObject(SubMenuObject
);
1144 EngSetLastError( ERROR_INVALID_PARAMETER
);
1149 { // If submenu just dereference it.
1150 if (MenuItem
->spSubMenu
) IntReleaseMenuObject(MenuItem
->spSubMenu
);
1151 MenuItem
->spSubMenu
= NULL
;
1155 if ((lpmii
->fMask
& MIIM_STRING
) ||
1156 ((lpmii
->fMask
& MIIM_TYPE
) && (MENU_ITEM_TYPE(lpmii
->fType
) == MF_STRING
)))
1158 /* free the string when used */
1159 FreeMenuText(MenuObject
,MenuItem
);
1160 RtlInitUnicodeString(&MenuItem
->lpstr
, NULL
);
1161 MenuItem
->Xlpstr
= NULL
;
1163 if(lpmii
->dwTypeData
&& lpmii
->cch
&& lpstr
&& lpstr
->Buffer
)
1165 UNICODE_STRING Source
;
1167 Source
.Length
= Source
.MaximumLength
= lpmii
->cch
* sizeof(WCHAR
);
1168 Source
.Buffer
= lpmii
->dwTypeData
;
1170 MenuItem
->lpstr
.Buffer
= DesktopHeapAlloc( MenuObject
->head
.rpdesk
, Source
.Length
+ sizeof(WCHAR
));
1171 if(MenuItem
->lpstr
.Buffer
!= NULL
)
1173 MenuItem
->lpstr
.Length
= 0;
1174 MenuItem
->lpstr
.MaximumLength
= Source
.Length
+ sizeof(WCHAR
);
1175 RtlCopyUnicodeString(&MenuItem
->lpstr
, &Source
);
1176 MenuItem
->lpstr
.Buffer
[MenuItem
->lpstr
.Length
/ sizeof(WCHAR
)] = 0;
1178 MenuItem
->cch
= MenuItem
->lpstr
.Length
/ sizeof(WCHAR
);
1179 MenuItem
->Xlpstr
= (USHORT
*)MenuItem
->lpstr
.Buffer
;
1184 if( !(MenuObject
->fFlags
& MNF_SYSMENU
) &&
1185 !MenuItem
->Xlpstr
&&
1186 !lpmii
->dwTypeData
&&
1187 !(MenuItem
->fType
& MFT_OWNERDRAW
) &&
1189 MenuItem
->fType
|= MFT_SEPARATOR
;
1191 if (sizeof(ROSMENUITEMINFO
) == lpmii
->cbSize
)
1193 MenuItem
->xItem
= lpmii
->Rect
.left
;
1194 MenuItem
->yItem
= lpmii
->Rect
.top
;
1195 MenuItem
->cxItem
= lpmii
->Rect
.right
; // Do this for now......
1196 MenuItem
->cyItem
= lpmii
->Rect
.bottom
;
1197 MenuItem
->dxTab
= lpmii
->dxTab
;
1198 lpmii
->lpstr
= MenuItem
->lpstr
.Buffer
; /* Send back new allocated string or zero */
1199 MenuItem
->cxBmp
= lpmii
->maxBmpSize
.cx
;
1200 MenuItem
->cyBmp
= lpmii
->maxBmpSize
.cy
;
1208 IntEnableMenuItem(PMENU MenuObject
, UINT uIDEnableItem
, UINT uEnable
)
1213 if (!(MenuItem
= MENU_FindItem( &MenuObject
, &uIDEnableItem
, uEnable
))) return (UINT
)-1;
1215 res
= MenuItem
->fState
& (MF_GRAYED
| MF_DISABLED
);
1217 MenuItem
->fState
^= (res
^ uEnable
) & (MF_GRAYED
| MF_DISABLED
);
1219 /* If the close item in the system menu change update the close button */
1222 switch (MenuItem
->wID
) // More than just close.
1230 if (MenuObject
->fFlags
& MNF_SYSSUBMENU
&& MenuObject
->spwndNotify
!= 0)
1232 //RECTL rc = MenuObject->spwndNotify->rcWindow;
1234 /* Refresh the frame to reflect the change */
1235 //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1237 //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1240 UserPaintCaption(MenuObject
->spwndNotify
, DC_BUTTONS
);
1250 IntCheckMenuItem(PMENU MenuObject
, UINT uIDCheckItem
, UINT uCheck
)
1255 if (!(MenuItem
= MENU_FindItem( &MenuObject
, &uIDCheckItem
, uCheck
))) return -1;
1257 res
= (DWORD
)(MenuItem
->fState
& MF_CHECKED
);
1259 MenuItem
->fState
^= (res
^ uCheck
) & MF_CHECKED
;
1265 UserSetMenuDefaultItem(PMENU MenuObject
, UINT uItem
, UINT fByPos
)
1268 PITEM MenuItem
= MenuObject
->rgItems
;
1270 if (!MenuItem
) return FALSE
;
1272 /* reset all default-item flags */
1273 for (i
= 0; i
< MenuObject
->cItems
; i
++, MenuItem
++)
1275 MenuItem
->fState
&= ~MFS_DEFAULT
;
1278 /* no default item */
1279 if(uItem
== (UINT
)-1)
1283 MenuItem
= MenuObject
->rgItems
;
1286 if ( uItem
>= MenuObject
->cItems
) return FALSE
;
1287 MenuItem
[uItem
].fState
|= MFS_DEFAULT
;
1292 for (i
= 0; i
< MenuObject
->cItems
; i
++, MenuItem
++)
1294 if (MenuItem
->wID
== uItem
)
1296 MenuItem
->fState
|= MFS_DEFAULT
;
1306 IntGetMenuDefaultItem(PMENU MenuObject
, UINT fByPos
, UINT gmdiFlags
, DWORD
*gismc
)
1309 PITEM MenuItem
= MenuObject
->rgItems
;
1312 if (!MenuItem
) return -1;
1314 while ( !( MenuItem
->fState
& MFS_DEFAULT
) )
1317 if (i
>= MenuObject
->cItems
) return -1;
1320 /* default: don't return disabled items */
1321 if ( (!(GMDI_USEDISABLED
& gmdiFlags
)) && (MenuItem
->fState
& MFS_DISABLED
)) return -1;
1323 /* search rekursiv when needed */
1324 if ( (gmdiFlags
& GMDI_GOINTOPOPUPS
) && MenuItem
->spSubMenu
)
1328 ret
= IntGetMenuDefaultItem( MenuItem
->spSubMenu
, fByPos
, gmdiFlags
, gismc
);
1330 if ( -1 != ret
) return ret
;
1332 /* when item not found in submenu, return the popup item */
1334 return ( fByPos
) ? i
: MenuItem
->wID
;
1344 if (!(pItem
= MENU_FindItem( &pMenu
, (UINT
*)&nPos
, MF_BYPOSITION
))) return NULL
;
1345 return pItem
->spSubMenu
;
1348 /***********************************************************************
1349 * MenuInitSysMenuPopup
1351 * Grey the appropriate items in System menu.
1353 void FASTCALL
MENU_InitSysMenuPopup(PMENU menu
, DWORD style
, DWORD clsStyle
, LONG HitTest
)
1358 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1359 IntEnableMenuItem( menu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1360 gray
= ((style
& WS_MAXIMIZE
) != 0);
1361 IntEnableMenuItem( menu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1362 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
1363 IntEnableMenuItem( menu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1364 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
1365 IntEnableMenuItem( menu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1366 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
1367 IntEnableMenuItem( menu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
1368 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
1370 /* The menu item must keep its state if it's disabled */
1372 IntEnableMenuItem( menu
, SC_CLOSE
, MF_GRAYED
);
1374 /* Set default menu item */
1375 if(style
& WS_MINIMIZE
) DefItem
= SC_RESTORE
;
1376 else if(HitTest
== HTCAPTION
) DefItem
= ((style
& (WS_MAXIMIZE
| WS_MINIMIZE
)) ? SC_RESTORE
: SC_MAXIMIZE
);
1377 else DefItem
= SC_CLOSE
;
1379 UserSetMenuDefaultItem(menu
, DefItem
, MF_BYCOMMAND
);
1383 /***********************************************************************
1384 * MenuDrawPopupGlyph
1386 * Draws popup magic glyphs (can be found in system menu).
1388 static void FASTCALL
1389 MENU_DrawPopupGlyph(HDC dc
, LPRECT r
, INT_PTR popupMagic
, BOOL inactive
, BOOL hilite
)
1392 HFONT hFont
, hOldFont
;
1398 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1401 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1404 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1407 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1411 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic
);
1414 RtlZeroMemory(&lf
, sizeof(LOGFONTW
));
1415 RECTL_vInflateRect(r
, -2, -2);
1416 lf
.lfHeight
= r
->bottom
- r
->top
;
1418 lf
.lfWeight
= FW_NORMAL
;
1419 lf
.lfCharSet
= DEFAULT_CHARSET
;
1420 RtlCopyMemory(lf
.lfFaceName
, L
"Marlett", sizeof(L
"Marlett"));
1421 hFont
= GreCreateFontIndirectW(&lf
);
1422 /* save font and text color */
1423 hOldFont
= NtGdiSelectFont(dc
, hFont
);
1424 clrsave
= GreGetTextColor(dc
);
1425 bkmode
= GreGetBkMode(dc
);
1426 /* set color and drawing mode */
1427 IntGdiSetBkMode(dc
, TRANSPARENT
);
1433 IntGdiSetTextColor(dc
, IntGetSysColor(COLOR_HIGHLIGHTTEXT
));
1434 GreTextOutW(dc
, r
->left
+ 1, r
->top
+ 1, &symbol
, 1);
1437 IntGdiSetTextColor(dc
, IntGetSysColor(inactive
? COLOR_GRAYTEXT
: (hilite
? COLOR_HIGHLIGHTTEXT
: COLOR_MENUTEXT
)));
1438 /* draw selected symbol */
1439 GreTextOutW(dc
, r
->left
, r
->top
, &symbol
, 1);
1440 /* restore previous settings */
1441 IntGdiSetTextColor(dc
, clrsave
);
1442 NtGdiSelectFont(dc
, hOldFont
);
1443 IntGdiSetBkMode(dc
, bkmode
);
1444 GreDeleteObject(hFont
);
1447 /***********************************************************************
1448 * MENU_AdjustMenuItemRect
1450 * Adjust menu item rectangle according to scrolling state.
1453 MENU_AdjustMenuItemRect(PMENU menu
, PRECTL rect
)
1455 if (menu
->dwArrowsOn
)
1457 UINT arrow_bitmap_height
;
1458 arrow_bitmap_height
= gpsi
->oembmi
[OBI_UPARROW
].cy
; ///// Menu up arrow! OBM_UPARROW
1459 rect
->top
+= arrow_bitmap_height
- menu
->iTop
;
1460 rect
->bottom
+= arrow_bitmap_height
- menu
->iTop
;
1464 /***********************************************************************
1465 * MENU_FindItemByCoords
1467 * Find the item at the specified coordinates (screen coords). Does
1468 * not work for child windows and therefore should not be called for
1469 * an arbitrary system menu.
1471 static ITEM
*MENU_FindItemByCoords( MENU
*menu
, POINT pt
, UINT
*pos
)
1476 PWND pWnd
= ValidateHwndNoErr(menu
->hWnd
);
1478 if (!IntGetWindowRect(pWnd
, &rect
)) return NULL
;
1479 if (pWnd
->ExStyle
& WS_EX_LAYOUTRTL
)
1480 pt
.x
= rect
.right
- 1 - pt
.x
;
1484 item
= menu
->rgItems
;
1485 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
1487 //rect = item->rect;
1488 rect
.left
= item
->xItem
;
1489 rect
.top
= item
->yItem
;
1490 rect
.right
= item
->cxItem
; // Do this for now......
1491 rect
.bottom
= item
->cyItem
;
1493 MENU_AdjustMenuItemRect(menu
, &rect
);
1494 if (RECTL_bPointInRect(&rect
, pt
.x
, pt
.y
))
1503 INT FASTCALL
IntMenuItemFromPoint(PWND pWnd
, HMENU hMenu
, POINT ptScreen
)
1505 MENU
*menu
= UserGetMenuObject(hMenu
);
1508 /*FIXME: Do we have to handle hWnd here? */
1509 if (!menu
) return -1;
1510 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
1514 /***********************************************************************
1517 * Find the menu item selected by a key press.
1518 * Return item id, -1 if none, -2 if we should close the menu.
1520 static UINT FASTCALL
MENU_FindItemByKey(PWND WndOwner
, PMENU menu
,
1521 WCHAR Key
, BOOL ForceMenuChar
)
1526 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key
, Key
, menu
);
1528 if (!menu
|| !VerifyMenu(menu
))
1529 menu
= co_IntGetSubMenu( UserGetMenuObject(WndOwner
->SystemMenu
), 0 );
1532 ITEM
*item
= menu
->rgItems
;
1534 if ( !ForceMenuChar
)
1537 BOOL cjk
= UserGetSystemMetrics( SM_DBCSENABLED
);
1539 for (i
= 0; i
< menu
->cItems
; i
++, item
++)
1541 LPWSTR text
= item
->Xlpstr
;
1544 const WCHAR
*p
= text
- 2;
1547 const WCHAR
*q
= p
+ 2;
1548 p
= wcschr (q
, '&');
1549 if (!p
&& cjk
) p
= wcschr (q
, '\036'); /* Japanese Win16 */
1551 while (p
!= NULL
&& p
[1] == '&');
1552 if (p
&& (towupper(p
[1]) == towupper(Key
))) return i
;
1557 Flags
|= menu
->fFlags
& MNF_POPUP
? MF_POPUP
: 0;
1558 Flags
|= menu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0;
1560 MenuChar
= co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_MENUCHAR
,
1561 MAKEWPARAM(Key
, Flags
), (LPARAM
) UserHMGetHandle(menu
));
1562 if (HIWORD(MenuChar
) == MNC_EXECUTE
) return LOWORD(MenuChar
);
1563 if (HIWORD(MenuChar
) == MNC_CLOSE
) return (UINT
)(-2);
1568 /***********************************************************************
1569 * MenuGetBitmapItemSize
1571 * Get the size of a bitmap item.
1573 static void FASTCALL
MENU_GetBitmapItemSize(PITEM lpitem
, SIZE
*size
, PWND WndOwner
)
1576 HBITMAP bmp
= lpitem
->hbmp
;
1578 size
->cx
= size
->cy
= 0;
1580 /* check if there is a magic menu item associated with this item */
1581 if (IS_MAGIC_BITMAP(bmp
))
1583 switch((INT_PTR
) bmp
)
1585 case (INT_PTR
)HBMMENU_CALLBACK
:
1587 MEASUREITEMSTRUCT measItem
;
1588 measItem
.CtlType
= ODT_MENU
;
1590 measItem
.itemID
= lpitem
->wID
;
1591 measItem
.itemWidth
= lpitem
->cxItem
- lpitem
->xItem
; //lpitem->Rect.right - lpitem->Rect.left;
1592 measItem
.itemHeight
= lpitem
->cyItem
- lpitem
->yItem
; //lpitem->Rect.bottom - lpitem->Rect.top;
1593 measItem
.itemData
= lpitem
->dwItemData
;
1594 co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
1595 size
->cx
= measItem
.itemWidth
;
1596 size
->cy
= measItem
.itemHeight
;
1597 TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem
.itemHeight
,measItem
.itemWidth
);
1602 case (INT_PTR
) HBMMENU_SYSTEM
:
1603 if (lpitem
->dwItemData
)
1605 bmp
= (HBITMAP
) lpitem
->dwItemData
;
1609 case (INT_PTR
) HBMMENU_MBAR_RESTORE
:
1610 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE
:
1611 case (INT_PTR
) HBMMENU_MBAR_CLOSE
:
1612 case (INT_PTR
) HBMMENU_MBAR_MINIMIZE_D
:
1613 case (INT_PTR
) HBMMENU_MBAR_CLOSE_D
:
1614 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1615 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1616 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1617 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1618 /* FIXME: Why we need to subtract these magic values? */
1619 /* to make them smaller than the menu bar? */
1620 size
->cx
= UserGetSystemMetrics(SM_CXSIZE
) - 2;
1621 size
->cy
= UserGetSystemMetrics(SM_CYSIZE
) - 4;
1626 if (GreGetObject(bmp
, sizeof(BITMAP
), &bm
))
1628 size
->cx
= bm
.bmWidth
;
1629 size
->cy
= bm
.bmHeight
;
1633 /***********************************************************************
1634 * MenuDrawBitmapItem
1636 * Draw a bitmap item.
1638 static void FASTCALL
MENU_DrawBitmapItem(HDC hdc
, PITEM lpitem
, const RECT
*rect
,
1639 PMENU Menu
, PWND WndOwner
, UINT odaction
, BOOL MenuBar
)
1645 int w
= rect
->right
- rect
->left
;
1646 int h
= rect
->bottom
- rect
->top
;
1647 int bmp_xoffset
= 0;
1649 HBITMAP hbmToDraw
= lpitem
->hbmp
;
1652 /* Check if there is a magic menu item associated with this item */
1653 if (IS_MAGIC_BITMAP(hbmToDraw
))
1659 switch ((INT_PTR
)hbmToDraw
)
1661 case (INT_PTR
)HBMMENU_SYSTEM
:
1662 if (lpitem
->dwItemData
)
1664 if (ValidateHwndNoErr((HWND
)lpitem
->dwItemData
))
1666 ERR("Get Item Data from this Window!!!\n");
1669 ERR("Draw Bitmap\n");
1670 bmp
= (HBITMAP
)lpitem
->dwItemData
;
1671 if (!GreGetObject( bmp
, sizeof(bm
), &bm
)) return;
1675 PCURICON_OBJECT pIcon
= NULL
;
1676 //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1678 //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1679 /* only use right half of the bitmap */
1680 //bmp_xoffset = bm.bmWidth / 2;
1681 //bm.bmWidth -= bmp_xoffset;
1684 pIcon
= NC_IconForWindow(WndOwner
);
1685 // FIXME: NC_IconForWindow should reference it for us */
1686 if (pIcon
) UserReferenceObject(pIcon
);
1691 LONG cx
= UserGetSystemMetrics(SM_CXSMICON
);
1692 LONG cy
= UserGetSystemMetrics(SM_CYSMICON
);
1693 LONG x
= rect
->left
- cx
/2 + 1 + (rect
->bottom
- rect
->top
)/2; // this is really what Window does
1694 LONG y
= (rect
->top
+ rect
->bottom
)/2 - cy
/2; // center
1695 UserDrawIconEx(hdc
, x
, y
, pIcon
, cx
, cy
, 0, NULL
, DI_NORMAL
);
1696 UserDereferenceObject(pIcon
);
1701 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
1702 flags
= DFCS_CAPTIONRESTORE
;
1704 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
1706 flags
= DFCS_CAPTIONMIN
;
1708 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
1710 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
1712 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
1713 flags
= DFCS_CAPTIONCLOSE
;
1715 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
1716 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
1718 case (INT_PTR
)HBMMENU_CALLBACK
:
1720 DRAWITEMSTRUCT drawItem
;
1722 drawItem
.CtlType
= ODT_MENU
;
1724 drawItem
.itemID
= lpitem
->wID
;
1725 drawItem
.itemAction
= odaction
;
1726 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
1727 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
1728 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
1729 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
1730 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
1731 drawItem
.itemState
|= (!(Menu
->fFlags
& MNF_UNDERLINE
))?ODS_NOACCEL
:0;
1732 drawItem
.itemState
|= (Menu
->fFlags
& MNF_INACTIVE
)?ODS_INACTIVE
:0;
1733 drawItem
.hwndItem
= (HWND
)UserHMGetHandle(Menu
);
1735 drawItem
.rcItem
= *rect
;
1736 drawItem
.itemData
= lpitem
->dwItemData
;
1737 /* some applications make this assumption on the DC's origin */
1738 GreSetViewportOrgEx( hdc
, lpitem
->xItem
, lpitem
->yItem
, &origorg
);
1739 RECTL_vOffsetRect( &drawItem
.rcItem
, - lpitem
->xItem
, - lpitem
->yItem
);
1740 co_IntSendMessage( UserHMGetHandle(WndOwner
), WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
1741 GreSetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1746 case (INT_PTR
) HBMMENU_POPUP_CLOSE
:
1747 case (INT_PTR
) HBMMENU_POPUP_RESTORE
:
1748 case (INT_PTR
) HBMMENU_POPUP_MAXIMIZE
:
1749 case (INT_PTR
) HBMMENU_POPUP_MINIMIZE
:
1750 MENU_DrawPopupGlyph(hdc
, &r
, (INT_PTR
)hbmToDraw
, lpitem
->fState
& MF_GRAYED
, lpitem
->fState
& MF_HILITE
);
1753 RECTL_vInflateRect(&r
, -1, -1);
1754 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
1755 DrawFrameControl(hdc
, &r
, DFC_CAPTION
, flags
);
1759 if (!bmp
|| !GreGetObject( bmp
, sizeof(bm
), &bm
)) return;
1762 hdcMem
= NtGdiCreateCompatibleDC( hdc
);
1763 NtGdiSelectBitmap( hdcMem
, bmp
);
1764 /* handle fontsize > bitmap_height */
1765 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
1767 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
1768 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmp
)
1769 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHT
));
1770 NtGdiBitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
, 0, 0);
1771 IntGdiDeleteDC( hdcMem
, FALSE
);
1775 IntGetDialogBaseUnits(VOID
)
1784 if ((hdc
= UserGetDCEx(NULL
, NULL
, DCX_CACHE
)))
1786 size
.cx
= IntGetCharDimensions( hdc
, NULL
, (PDWORD
)&size
.cy
);
1787 if (size
.cx
) units
= MAKELONG( size
.cx
, size
.cy
);
1788 UserReleaseDC( 0, hdc
, FALSE
);
1795 /***********************************************************************
1798 * Calculate the size of the menu item and store it in lpitem->rect.
1800 static void FASTCALL
MENU_CalcItemSize( HDC hdc
, PITEM lpitem
, PMENU Menu
, PWND pwndOwner
,
1801 INT orgX
, INT orgY
, BOOL menuBar
, BOOL textandbmp
)
1804 UINT check_bitmap_width
= UserGetSystemMetrics( SM_CXMENUCHECK
);
1805 UINT arrow_bitmap_width
;
1809 TRACE("dc=%x owner=%x (%d,%d)\n", hdc
, pwndOwner
, orgX
, orgY
);
1811 arrow_bitmap_width
= gpsi
->oembmi
[OBI_MNARROW
].cx
;
1813 MenuCharSize
.cx
= IntGetCharDimensions( hdc
, NULL
, (PDWORD
)&MenuCharSize
.cy
);
1815 RECTL_vSetRect( &Rect
, orgX
, orgY
, orgX
, orgY
);
1817 if (lpitem
->fType
& MF_OWNERDRAW
)
1819 MEASUREITEMSTRUCT mis
;
1820 mis
.CtlType
= ODT_MENU
;
1822 mis
.itemID
= lpitem
->wID
;
1823 mis
.itemData
= lpitem
->dwItemData
;
1824 mis
.itemHeight
= HIWORD( IntGetDialogBaseUnits());
1826 co_IntSendMessage( UserHMGetHandle(pwndOwner
), WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1827 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1828 * width of a menufont character to the width of an owner-drawn menu.
1830 Rect
.right
+= mis
.itemWidth
+ 2 * MenuCharSize
.cx
;
1832 /* under at least win95 you seem to be given a standard
1833 height for the menu and the height value is ignored */
1834 Rect
.bottom
+= UserGetSystemMetrics(SM_CYMENUSIZE
);
1836 Rect
.bottom
+= mis
.itemHeight
;
1838 //lpitem->cxBmp = mis.itemWidth;
1839 //lpitem->cyBmp = mis.itemHeight;
1840 TRACE("MF_OWNERDRAW Height %d Width %d\n",mis
.itemHeight
,mis
.itemWidth
);
1841 TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1842 lpitem
->wID
, Rect
.right
-Rect
.left
,
1843 Rect
.bottom
-Rect
.top
, MenuCharSize
.cx
, MenuCharSize
.cy
);
1845 lpitem
->xItem
= Rect
.left
;
1846 lpitem
->yItem
= Rect
.top
;
1847 lpitem
->cxItem
= Rect
.right
;
1848 lpitem
->cyItem
= Rect
.bottom
;
1853 lpitem
->xItem
= orgX
;
1854 lpitem
->yItem
= orgY
;
1855 lpitem
->cxItem
= orgX
;
1856 lpitem
->cyItem
= orgY
;
1858 if (lpitem
->fType
& MF_SEPARATOR
)
1860 lpitem
->cyItem
+= UserGetSystemMetrics( SM_CYMENUSIZE
)/2;//SEPARATOR_HEIGHT;
1862 lpitem
->cxItem
+= arrow_bitmap_width
+ MenuCharSize
.cx
;
1873 MENU_GetBitmapItemSize(lpitem
, &size
, pwndOwner
);
1874 /* Keep the size of the bitmap in callback mode to be able
1875 * to draw it correctly */
1876 lpitem
->cxBmp
= size
.cx
;
1877 lpitem
->cyBmp
= size
.cy
;
1878 Menu
->cxTextAlign
= max(Menu
->cxTextAlign
, size
.cx
);
1879 lpitem
->cxItem
+= size
.cx
+ 2;
1880 itemheight
= size
.cy
+ 2;
1882 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
1883 lpitem
->cxItem
+= 2 * check_bitmap_width
;
1884 lpitem
->cxItem
+= 4 + MenuCharSize
.cx
;
1885 lpitem
->dxTab
= lpitem
->cxItem
;
1886 lpitem
->cxItem
+= arrow_bitmap_width
;
1887 } else /* hbmpItem & MenuBar */ {
1888 MENU_GetBitmapItemSize(lpitem
, &size
, pwndOwner
);
1889 lpitem
->cxItem
+= size
.cx
;
1890 if( lpitem
->Xlpstr
) lpitem
->cxItem
+= 2;
1891 itemheight
= size
.cy
;
1893 /* Special case: Minimize button doesn't have a space behind it. */
1894 if (lpitem
->hbmp
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE
||
1895 lpitem
->hbmp
== (HBITMAP
)HBMMENU_MBAR_MINIMIZE_D
)
1896 lpitem
->cxItem
-= 1;
1899 else if (!menuBar
) {
1900 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
1901 lpitem
->cxItem
+= check_bitmap_width
;
1902 lpitem
->cxItem
+= 4 + MenuCharSize
.cx
;
1903 lpitem
->dxTab
= lpitem
->cxItem
;
1904 lpitem
->cxItem
+= arrow_bitmap_width
;
1907 /* it must be a text item - unless it's the system menu */
1908 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->Xlpstr
) {
1909 HFONT hfontOld
= NULL
;
1910 RECT rc
;// = lpitem->Rect;
1911 LONG txtheight
, txtwidth
;
1913 rc
.left
= lpitem
->xItem
;
1914 rc
.top
= lpitem
->yItem
;
1915 rc
.right
= lpitem
->cxItem
; // Do this for now......
1916 rc
.bottom
= lpitem
->cyItem
;
1918 if ( lpitem
->fState
& MFS_DEFAULT
) {
1919 hfontOld
= NtGdiSelectFont( hdc
, ghMenuFontBold
);
1922 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, -1, &rc
, DT_SINGLELINE
|DT_CALCRECT
);
1924 lpitem
->cxItem
+= rc
.right
- rc
.left
;
1925 itemheight
= max( max( itemheight
, txtheight
), UserGetSystemMetrics( SM_CYMENU
) - 1);
1927 lpitem
->cxItem
+= 2 * MenuCharSize
.cx
;
1929 if ((p
= wcschr( lpitem
->Xlpstr
, '\t' )) != NULL
) {
1932 int n
= (int)( p
- lpitem
->Xlpstr
);
1933 /* Item contains a tab (only meaningful in popup menus) */
1934 /* get text size before the tab */
1935 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, n
, &rc
,
1936 DT_SINGLELINE
|DT_CALCRECT
);
1937 txtwidth
= rc
.right
- rc
.left
;
1938 p
+= 1; /* advance past the Tab */
1939 /* get text size after the tab */
1940 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1941 DT_SINGLELINE
|DT_CALCRECT
);
1942 lpitem
->dxTab
+= txtwidth
;
1943 txtheight
= max( txtheight
, tmpheight
);
1944 txtwidth
+= MenuCharSize
.cx
+ /* space for the tab */
1945 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1947 txtheight
= DrawTextW( hdc
, lpitem
->Xlpstr
, -1, &rc
,
1948 DT_SINGLELINE
|DT_CALCRECT
);
1949 txtwidth
= rc
.right
- rc
.left
;
1950 lpitem
->dxTab
+= txtwidth
;
1952 lpitem
->cxItem
+= 2 + txtwidth
;
1953 itemheight
= max( itemheight
,
1954 max( txtheight
+ 2, MenuCharSize
.cy
+ 4));
1958 NtGdiSelectFont (hdc
, hfontOld
);
1960 } else if( menuBar
) {
1961 itemheight
= max( itemheight
, UserGetSystemMetrics(SM_CYMENU
)-1);
1963 lpitem
->cyItem
+= itemheight
;
1964 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->xItem
, lpitem
->yItem
, lpitem
->cxItem
, lpitem
->cyItem
);
1967 /***********************************************************************
1968 * MENU_GetMaxPopupHeight
1971 MENU_GetMaxPopupHeight(PMENU lppop
)
1975 //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1976 return lppop
->cyMax
;
1978 //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
1979 return UserGetSystemMetrics(SM_CYSCREEN
) - UserGetSystemMetrics(SM_CYBORDER
);
1982 /***********************************************************************
1983 * MenuPopupMenuCalcSize
1985 * Calculate the size of a popup menu.
1987 static void FASTCALL
MENU_PopupMenuCalcSize(PMENU Menu
, PWND WndOwner
)
1992 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1993 BOOL textandbmp
= FALSE
;
1995 Menu
->cxMenu
= Menu
->cyMenu
= 0;
1996 if (Menu
->cItems
== 0) return;
1998 hdc
= UserGetDCEx(NULL
, NULL
, DCX_CACHE
);
2000 NtGdiSelectFont( hdc
, ghMenuFont
);
2005 Menu
->cxTextAlign
= 0;
2007 while (start
< Menu
->cItems
)
2009 lpitem
= &Menu
->rgItems
[start
];
2011 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
2012 orgX
+= MENU_COL_SPACE
;
2013 orgY
= MENU_TOP_MARGIN
;
2015 maxTab
= maxTabWidth
= 0;
2016 /* Parse items until column break or end of menu */
2017 for (i
= start
; i
< Menu
->cItems
; i
++, lpitem
++)
2020 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
2022 MENU_CalcItemSize(hdc
, lpitem
, Menu
, WndOwner
, orgX
, orgY
, FALSE
, textandbmp
);
2023 maxX
= max(maxX
, lpitem
->cxItem
);
2024 orgY
= lpitem
->cyItem
;
2025 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->dxTab
)
2027 maxTab
= max( maxTab
, lpitem
->dxTab
);
2028 maxTabWidth
= max(maxTabWidth
, lpitem
->cxItem
- lpitem
->dxTab
);
2030 if( lpitem
->Xlpstr
&& lpitem
->hbmp
) textandbmp
= TRUE
;
2033 /* Finish the column (set all items to the largest width found) */
2034 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
2035 for (lpitem
= &Menu
->rgItems
[start
]; start
< i
; start
++, lpitem
++)
2037 lpitem
->cxItem
= maxX
;
2038 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->dxTab
)
2039 lpitem
->dxTab
= maxTab
;
2041 Menu
->cyMenu
= max(Menu
->cyMenu
, orgY
);
2044 Menu
->cxMenu
= maxX
;
2045 /* if none of the items have both text and bitmap then
2046 * the text and bitmaps are all aligned on the left. If there is at
2047 * least one item with both text and bitmap then bitmaps are
2048 * on the left and texts left aligned with the right hand side
2050 if( !textandbmp
) Menu
->cxTextAlign
= 0;
2052 /* space for 3d border */
2053 Menu
->cyMenu
+= MENU_BOTTOM_MARGIN
;
2056 /* Adjust popup height if it exceeds maximum */
2057 maxHeight
= MENU_GetMaxPopupHeight(Menu
);
2058 Menu
->iMaxTop
= Menu
->cyMenu
- MENU_TOP_MARGIN
;
2059 if (Menu
->cyMenu
>= maxHeight
)
2061 Menu
->cyMenu
= maxHeight
;
2062 Menu
->dwArrowsOn
= 1;
2066 Menu
->dwArrowsOn
= 0;
2068 UserReleaseDC( 0, hdc
, FALSE
);
2071 /***********************************************************************
2072 * MENU_MenuBarCalcSize
2074 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2075 * height is off by 1 pixel which causes lengthy window relocations when
2076 * active document window is maximized/restored.
2078 * Calculate the size of the menu bar.
2080 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
, PMENU lppop
, PWND pwndOwner
)
2083 UINT start
, i
, helpPos
;
2084 int orgX
, orgY
, maxY
;
2086 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
2087 if (lppop
->cItems
== 0) return;
2088 //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2089 lppop
->cxMenu
= lprect
->right
- lprect
->left
;
2091 maxY
= lprect
->top
+1;
2094 lppop
->cxTextAlign
= 0;
2095 while (start
< lppop
->cItems
)
2097 lpitem
= &lppop
->rgItems
[start
];
2098 orgX
= lprect
->left
;
2101 /* Parse items until line break or end of menu */
2102 for (i
= start
; i
< lppop
->cItems
; i
++, lpitem
++)
2104 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
2106 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
2108 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
2109 //debug_print_menuitem (" item: ", lpitem, "");
2110 //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2111 MENU_CalcItemSize(hdc
, lpitem
, lppop
, pwndOwner
, orgX
, orgY
, TRUE
, FALSE
);
2113 if (lpitem
->cxItem
> lprect
->right
)
2115 if (i
!= start
) break;
2116 else lpitem
->cxItem
= lprect
->right
;
2118 maxY
= max( maxY
, lpitem
->cyItem
);
2119 orgX
= lpitem
->cxItem
;
2122 /* Finish the line (set all items to the largest height found) */
2124 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2125 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2127 while (start
< i
) lppop
->rgItems
[start
++].cyItem
= maxY
;
2129 start
= i
; /* This works! */
2132 lprect
->bottom
= maxY
;
2133 lppop
->cyMenu
= lprect
->bottom
- lprect
->top
;
2135 /* Flush right all items between the MF_RIGHTJUSTIFY and */
2136 /* the last item (if several lines, only move the last line) */
2137 if (helpPos
== ~0U) return;
2138 lpitem
= &lppop
->rgItems
[lppop
->cItems
-1];
2139 orgY
= lpitem
->yItem
;
2140 orgX
= lprect
->right
;
2141 for (i
= lppop
->cItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
2142 if (lpitem
->yItem
!= orgY
) break; /* Other line */
2143 if (lpitem
->cxItem
>= orgX
) break; /* Too far right already */
2144 lpitem
->xItem
+= orgX
- lpitem
->cxItem
;
2145 lpitem
->cxItem
= orgX
;
2146 orgX
= lpitem
->xItem
;
2150 /***********************************************************************
2151 * MENU_DrawScrollArrows
2153 * Draw scroll arrows.
2155 static void MENU_DrawScrollArrows(PMENU lppop
, HDC hdc
)
2157 UINT arrow_bitmap_width
, arrow_bitmap_height
;
2161 arrow_bitmap_width
= gpsi
->oembmi
[OBI_DNARROW
].cx
;
2162 arrow_bitmap_height
= gpsi
->oembmi
[OBI_DNARROW
].cy
;
2166 rect
.right
= lppop
->cxMenu
;
2167 rect
.bottom
= arrow_bitmap_height
;
2168 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENU
));
2169 dfcrc
.left
= (lppop
->cxMenu
- arrow_bitmap_width
) / 2;
2171 dfcrc
.right
= arrow_bitmap_width
;
2172 dfcrc
.bottom
= arrow_bitmap_height
;
2173 DrawFrameControl(hdc
, &dfcrc
, DFC_MENU
, (lppop
->iTop
? 0 : DFCS_INACTIVE
)|DFCS_MENUARROWUP
);
2175 rect
.top
= lppop
->cyMenu
- arrow_bitmap_height
;
2176 rect
.bottom
= lppop
->cyMenu
;
2177 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENU
));
2178 if (!(lppop
->iTop
< lppop
->iMaxTop
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
)))
2179 Flags
= DFCS_INACTIVE
;
2180 dfcrc
.left
= (lppop
->cxMenu
- arrow_bitmap_width
) / 2;
2181 dfcrc
.top
= lppop
->cyMenu
- arrow_bitmap_height
;
2182 dfcrc
.right
= arrow_bitmap_width
;
2183 dfcrc
.bottom
= lppop
->cyMenu
;
2184 DrawFrameControl(hdc
, &dfcrc
, DFC_MENU
, Flags
|DFCS_MENUARROWDOWN
);
2187 /***********************************************************************
2190 * Draw a single menu item.
2192 static void FASTCALL
MENU_DrawMenuItem(PWND Wnd
, PMENU Menu
, PWND WndOwner
, HDC hdc
,
2193 PITEM lpitem
, UINT Height
, BOOL menuBar
, UINT odaction
)
2197 BOOL flat_menu
= FALSE
;
2199 UINT arrow_bitmap_width
= 0;
2203 arrow_bitmap_width
= gpsi
->oembmi
[OBI_MNARROW
].cx
;
2206 if (lpitem
->fType
& MF_SYSMENU
)
2208 if (!(Wnd
->style
& WS_MINIMIZE
))
2210 NC_GetInsideRect(Wnd
, &rect
);
2211 UserDrawSysMenuButton(Wnd
, hdc
, &rect
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
));
2216 UserSystemParametersInfo (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2217 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
2221 if (lpitem
->fState
& MF_HILITE
)
2223 if(menuBar
&& !flat_menu
) {
2224 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_MENUTEXT
));
2225 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_MENU
));
2227 if (lpitem
->fState
& MF_GRAYED
)
2228 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_GRAYTEXT
));
2230 IntGdiSetTextColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHTTEXT
));
2231 IntGdiSetBkColor(hdc
, IntGetSysColor(COLOR_HIGHLIGHT
));
2236 if (lpitem
->fState
& MF_GRAYED
)
2237 IntGdiSetTextColor( hdc
, IntGetSysColor( COLOR_GRAYTEXT
) );
2239 IntGdiSetTextColor( hdc
, IntGetSysColor( COLOR_MENUTEXT
) );
2240 IntGdiSetBkColor( hdc
, IntGetSysColor( bkgnd
) );
2243 //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2244 //rect = lpitem->Rect;
2245 rect
.left
= lpitem
->xItem
;
2246 rect
.top
= lpitem
->yItem
;
2247 rect
.right
= lpitem
->cxItem
; // Do this for now......
2248 rect
.bottom
= lpitem
->cyItem
;
2250 MENU_AdjustMenuItemRect(Menu
, &rect
);
2252 if (lpitem
->fType
& MF_OWNERDRAW
)
2255 ** Experimentation under Windows reveals that an owner-drawn
2256 ** menu is given the rectangle which includes the space it requested
2257 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2258 ** and a popup-menu arrow. This is the value of lpitem->rect.
2259 ** Windows will leave all drawing to the application except for
2260 ** the popup-menu arrow. Windows always draws that itself, after
2261 ** the menu owner has finished drawing.
2264 COLORREF old_bk
, old_text
;
2266 dis
.CtlType
= ODT_MENU
;
2268 dis
.itemID
= lpitem
->wID
;
2269 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
2271 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
2272 if (lpitem
->fState
& MF_DEFAULT
) dis
.itemState
|= ODS_DEFAULT
;
2273 if (lpitem
->fState
& MF_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
2274 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
| ODS_DISABLED
;
2275 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
2276 if (!(Menu
->fFlags
& MNF_UNDERLINE
)) dis
.itemState
|= ODS_NOACCEL
;
2277 if (Menu
->fFlags
& MNF_INACTIVE
) dis
.itemState
|= ODS_INACTIVE
;
2278 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2279 dis
.hwndItem
= (HWND
) UserHMGetHandle(Menu
);
2282 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2283 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd
,
2284 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
2285 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
2287 TRACE("Ownerdraw: Width %d Height %d\n", dis
.rcItem
.right
-dis
.rcItem
.left
, dis
.rcItem
.bottom
-dis
.rcItem
.top
);
2288 old_bk
= GreGetBkColor(hdc
);
2289 old_text
= GreGetTextColor(hdc
);
2290 co_IntSendMessage(UserHMGetHandle(WndOwner
), WM_DRAWITEM
, 0, (LPARAM
) &dis
);
2291 IntGdiSetBkColor(hdc
, old_bk
);
2292 IntGdiSetTextColor(hdc
, old_text
);
2293 /* Draw the popup-menu arrow */
2294 if (!menuBar
&& lpitem
->spSubMenu
)
2297 RtlCopyMemory(&rectTemp
, &rect
, sizeof(RECT
));
2298 rectTemp
.left
= rectTemp
.right
- UserGetSystemMetrics(SM_CXMENUCHECK
);
2299 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
2304 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
2306 if (lpitem
->fState
& MF_HILITE
)
2310 RECTL_vInflateRect (&rect
, -1, -1);
2311 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_MENUHILIGHT
));
2312 RECTL_vInflateRect (&rect
, 1, 1);
2313 FrameRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_HIGHLIGHT
));
2318 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
2320 FillRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_HIGHLIGHT
));
2324 FillRect( hdc
, &rect
, IntGetSysColorBrush(bkgnd
) );
2326 IntGdiSetBkMode( hdc
, TRANSPARENT
);
2328 /* vertical separator */
2329 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
2334 rc
.left
-= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2336 rc
.bottom
= Height
- 3;
2339 oldPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject(DC_PEN
) );
2340 IntSetDCPenColor(hdc
, IntGetSysColor(COLOR_BTNSHADOW
));
2341 GreMoveTo( hdc
, rc
.left
, rc
.top
, NULL
);
2342 NtGdiLineTo( hdc
, rc
.left
, rc
.bottom
);
2343 NtGdiSelectPen( hdc
, oldPen
);
2346 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
2349 /* horizontal separator */
2350 if (lpitem
->fType
& MF_SEPARATOR
)
2357 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
2360 oldPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject(DC_PEN
) );
2361 IntSetDCPenColor( hdc
, IntGetSysColor(COLOR_BTNSHADOW
));
2362 GreMoveTo( hdc
, rc
.left
, rc
.top
, NULL
);
2363 NtGdiLineTo( hdc
, rc
.right
, rc
.top
);
2364 NtGdiSelectPen( hdc
, oldPen
);
2367 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
2371 /* helper lines for debugging */
2372 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2373 FrameRect(hdc
, &rect
, NtGdiGetStockObject(BLACK_BRUSH
));
2374 NtGdiSelectPen(hdc
, NtGdiGetStockObject(DC_PEN
));
2375 IntSetDCPenColor(hdc
, IntGetSysColor(COLOR_WINDOWFRAME
));
2376 GreMoveTo(hdc
, rect
.left
, (rect
.top
+ rect
.bottom
) / 2, NULL
);
2377 NtGdiLineTo(hdc
, rect
.right
, (rect
.top
+ rect
.bottom
) / 2);
2379 #if 0 // breaks mdi menu bar icons.
2381 /* calculate the bitmap rectangle in coordinates relative
2382 * to the item rectangle */
2384 if( lpitem
->hbmp
== HBMMENU_CALLBACK
)
2387 bmprc
.left
= lpitem
->Xlpstr
? MenuCharSize
.cx
: 0;
2389 else if ((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
)
2391 else if ((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
)
2394 bmprc
.left
= 4 + UserGetSystemMetrics(SM_CXMENUCHECK
);
2396 bmprc
.right
= bmprc
.left
+ lpitem
->cxBmp
;
2398 if( menuBar
&& !(lpitem
->hbmp
== HBMMENU_CALLBACK
))
2401 bmprc
.top
= (rect
.bottom
- rect
.top
- lpitem
->cyBmp
) / 2;
2403 bmprc
.bottom
= bmprc
.top
+ lpitem
->cyBmp
;
2409 INT y
= rect
.top
+ rect
.bottom
;
2411 BOOL checked
= FALSE
;
2412 UINT check_bitmap_width
= UserGetSystemMetrics( SM_CXMENUCHECK
);
2413 UINT check_bitmap_height
= UserGetSystemMetrics( SM_CYMENUCHECK
);
2414 /* Draw the check mark
2417 * Custom checkmark bitmaps are monochrome but not always 1bpp.
2419 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
)) {
2420 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hbmpChecked
:
2421 lpitem
->hbmpUnchecked
;
2422 if (bm
) /* we have a custom bitmap */
2424 HDC hdcMem
= NtGdiCreateCompatibleDC( hdc
);
2426 NtGdiSelectBitmap( hdcMem
, bm
);
2427 NtGdiBitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
2428 check_bitmap_width
, check_bitmap_height
,
2429 hdcMem
, 0, 0, SRCCOPY
, 0,0);
2430 IntGdiDeleteDC( hdcMem
, FALSE
);
2433 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
2437 r
.right
= r
.left
+ check_bitmap_width
;
2438 DrawFrameControl( hdc
, &r
, DFC_MENU
,
2439 (lpitem
->fType
& MFT_RADIOCHECK
) ?
2440 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
2444 if ( lpitem
->hbmp
)//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2446 RECT bmpRect
= rect
;
2447 if (!((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
) && !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
2448 bmpRect
.left
+= check_bitmap_width
+ 2;
2449 if (!(checked
&& ((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
)))
2451 bmpRect
.right
= bmpRect
.left
+ lpitem
->cxBmp
;
2452 MENU_DrawBitmapItem(hdc
, lpitem
, &bmpRect
, Menu
, WndOwner
, odaction
, menuBar
);
2455 /* Draw the popup-menu arrow */
2456 if (lpitem
->spSubMenu
)
2459 RtlCopyMemory(&rectTemp
, &rect
, sizeof(RECT
));
2460 rectTemp
.left
= rectTemp
.right
- check_bitmap_width
;
2461 DrawFrameControl(hdc
, &rectTemp
, DFC_MENU
, DFCS_MENUARROW
);
2464 if( !((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_NOCHECK
))
2465 rect
.left
+= check_bitmap_width
;
2466 rect
.right
-= arrow_bitmap_width
;
2468 else if( lpitem
->hbmp
)
2469 { /* Draw the bitmap */
2470 MENU_DrawBitmapItem(hdc
, lpitem
, &rect
/*bmprc*/, Menu
, WndOwner
, odaction
, menuBar
);
2473 /* process text if present */
2479 UINT uFormat
= menuBar
?
2480 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
2481 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
2483 if (((Menu
->fFlags
& MNS_STYLE_MASK
) & MNS_CHECKORBMP
))
2484 rect
.left
+= max(0, (int)(Menu
->cxTextAlign
- UserGetSystemMetrics(SM_CXMENUCHECK
)));
2486 rect
.left
+= Menu
->cxTextAlign
;
2488 if ( lpitem
->fState
& MFS_DEFAULT
)
2490 hfontOld
= NtGdiSelectFont(hdc
, ghMenuFontBold
);
2495 rect
.left
+= lpitem
->cxBmp
;
2496 if( !(lpitem
->hbmp
== HBMMENU_CALLBACK
))
2497 rect
.left
+= MenuCharSize
.cx
;
2498 rect
.right
-= MenuCharSize
.cx
;
2501 Text
= lpitem
->Xlpstr
;
2504 for (i
= 0; Text
[i
]; i
++)
2505 if (Text
[i
] == L
'\t' || Text
[i
] == L
'\b')
2509 if(lpitem
->fState
& MF_GRAYED
)
2511 if (!(lpitem
->fState
& MF_HILITE
) )
2513 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
2514 IntGdiSetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
2515 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
2516 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
2518 IntGdiSetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
2520 DrawTextW( hdc
, Text
, i
, &rect
, uFormat
);
2522 /* paint the shortcut text */
2523 if (!menuBar
&& L
'\0' != Text
[i
]) /* There's a tab or flush-right char */
2525 if (L
'\t' == Text
[i
])
2527 rect
.left
= lpitem
->dxTab
;
2528 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
2532 rect
.right
= lpitem
->dxTab
;
2533 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
2536 if (lpitem
->fState
& MF_GRAYED
)
2538 if (!(lpitem
->fState
& MF_HILITE
) )
2540 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
2541 IntGdiSetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
2542 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
2543 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
2545 IntGdiSetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
2547 DrawTextW( hdc
, Text
+ i
+ 1, -1, &rect
, uFormat
);
2552 NtGdiSelectFont (hdc
, hfontOld
);
2557 /***********************************************************************
2560 * Paint a popup menu.
2562 static void FASTCALL
MENU_DrawPopupMenu(PWND wnd
, HDC hdc
, PMENU menu
)
2564 HBRUSH hPrevBrush
= 0, brush
= IntGetSysColorBrush(COLOR_MENU
);
2567 TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd
, hdc
, menu
);
2569 IntGetClientRect( wnd
, &rect
);
2571 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
2572 if((hPrevBrush
= NtGdiSelectBrush( hdc
, brush
))
2573 && (NtGdiSelectFont( hdc
, ghMenuFont
)))
2577 NtGdiRectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2579 hPrevPen
= NtGdiSelectPen( hdc
, NtGdiGetStockObject( NULL_PEN
) );
2582 BOOL flat_menu
= FALSE
;
2584 UserSystemParametersInfo (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2586 FrameRect(hdc
, &rect
, IntGetSysColorBrush(COLOR_BTNSHADOW
));
2588 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
2590 TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu
), (menu
->fFlags
& MNS_STYLE_MASK
));
2591 /* draw menu items */
2592 if (menu
&& menu
->cItems
)
2597 item
= menu
->rgItems
;
2598 for( u
= menu
->cItems
; u
> 0; u
--, item
++)
2600 MENU_DrawMenuItem(wnd
, menu
, menu
->spwndNotify
, hdc
, item
,
2601 menu
->cyMenu
, FALSE
, ODA_DRAWENTIRE
);
2603 /* draw scroll arrows */
2604 if (menu
->dwArrowsOn
)
2606 MENU_DrawScrollArrows(menu
, hdc
);
2612 NtGdiSelectBrush( hdc
, hPrevBrush
);
2617 /**********************************************************************
2620 PWND
MENU_IsMenuActive(VOID
)
2622 return ValidateHwndNoErr(top_popup
);
2625 /**********************************************************************
2628 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2630 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2632 void MENU_EndMenu( PWND pwnd
)
2635 menu
= UserGetMenuObject(top_popup_hmenu
);
2636 if ( menu
&& ( UserHMGetHandle(pwnd
) == menu
->hWnd
|| pwnd
== menu
->spwndNotify
) )
2638 if (fInsideMenuLoop
&& top_popup
)
2640 fInsideMenuLoop
= FALSE
;
2644 ERR("Already in End loop\n");
2649 UserPostMessage( top_popup
, WM_CANCELMODE
, 0, 0);
2655 IntDrawMenuBarTemp(PWND pWnd
, HDC hDC
, LPRECT Rect
, PMENU pMenu
, HFONT Font
)
2658 HFONT FontOld
= NULL
;
2659 BOOL flat_menu
= FALSE
;
2661 UserSystemParametersInfo(SPI_GETFLATMENU
, 0, &flat_menu
, 0);
2665 pMenu
= UserGetMenuObject(UlongToHandle(pWnd
->IDMenu
));
2673 if (Rect
== NULL
|| !pMenu
)
2675 return UserGetSystemMetrics(SM_CYMENU
);
2678 TRACE("(%x, %x, %p, %x, %x)\n", pWnd
, hDC
, Rect
, pMenu
, Font
);
2680 FontOld
= NtGdiSelectFont(hDC
, Font
);
2682 if (pMenu
->cyMenu
== 0)
2684 MENU_MenuBarCalcSize(hDC
, Rect
, pMenu
, pWnd
);
2687 Rect
->bottom
= Rect
->top
+ pMenu
->cyMenu
;
2689 FillRect(hDC
, Rect
, IntGetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
));
2691 NtGdiSelectPen(hDC
, NtGdiGetStockObject(DC_PEN
));
2692 IntSetDCPenColor(hDC
, IntGetSysColor(COLOR_3DFACE
));
2693 GreMoveTo(hDC
, Rect
->left
, Rect
->bottom
- 1, NULL
);
2694 NtGdiLineTo(hDC
, Rect
->right
, Rect
->bottom
- 1);
2696 if (pMenu
->cItems
== 0)
2698 NtGdiSelectFont(hDC
, FontOld
);
2699 return UserGetSystemMetrics(SM_CYMENU
);
2702 for (i
= 0; i
< pMenu
->cItems
; i
++)
2704 MENU_DrawMenuItem(pWnd
, pMenu
, pWnd
, hDC
, &pMenu
->rgItems
[i
], pMenu
->cyMenu
, TRUE
, ODA_DRAWENTIRE
);
2707 NtGdiSelectFont(hDC
, FontOld
);
2709 return pMenu
->cyMenu
;
2712 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, PWND pWnd
, BOOL suppress_draw
)
2715 PMENU lppop
= UserGetMenuObject(UlongToHandle(pWnd
->IDMenu
));
2719 // No menu. Do not reserve any space
2725 return UserGetSystemMetrics(SM_CYMENU
);
2730 hfontOld
= NtGdiSelectFont(hDC
, ghMenuFont
);
2732 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, pWnd
);
2734 lprect
->bottom
= lprect
->top
+ lppop
->cyMenu
;
2736 if (hfontOld
) NtGdiSelectFont( hDC
, hfontOld
);
2738 return lppop
->cyMenu
;
2742 return IntDrawMenuBarTemp(pWnd
, hDC
, lprect
, lppop
, NULL
);
2746 /***********************************************************************
2749 * Popup menu initialization before WM_ENTERMENULOOP.
2751 static BOOL
MENU_InitPopup( PWND pWndOwner
, PMENU menu
, UINT flags
)
2754 PPOPUPMENU pPopupMenu
;
2756 LARGE_STRING WindowName
;
2757 UNICODE_STRING ClassName
;
2758 DWORD ex_style
= WS_EX_TOOLWINDOW
;
2760 TRACE("owner=%p hmenu=%p\n", pWndOwner
, menu
);
2762 menu
->spwndNotify
= pWndOwner
;
2764 if (flags
& TPM_LAYOUTRTL
|| pWndOwner
->ExStyle
& WS_EX_LAYOUTRTL
)
2765 ex_style
= WS_EX_LAYOUTRTL
;
2767 ClassName
.Buffer
= WC_MENU
;
2768 ClassName
.Length
= 0;
2770 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2771 RtlZeroMemory(&Cs
, sizeof(Cs
));
2772 Cs
.style
= WS_POPUP
;
2773 Cs
.dwExStyle
= ex_style
;
2774 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2775 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
2776 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
2777 Cs
.lpCreateParams
= UserHMGetHandle(menu
);
2778 Cs
.hwndParent
= UserHMGetHandle(pWndOwner
);
2780 /* NOTE: In Windows, top menu popup is not owned. */
2781 pWndCreated
= co_UserCreateWindowEx( &Cs
, &ClassName
, &WindowName
, NULL
);
2783 if( !pWndCreated
) return FALSE
;
2786 // Setup pop up menu structure.
2788 menu
->hWnd
= UserHMGetHandle(pWndCreated
);
2790 pPopupMenu
= ((PMENUWND
)pWndCreated
)->ppopupmenu
;
2792 pPopupMenu
->spwndActivePopup
= pWndCreated
; // top_popup = MenuInfo.Wnd or menu->hWnd
2793 pPopupMenu
->spwndNotify
= pWndOwner
; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2794 //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2796 pPopupMenu
->fIsTrackPopup
= !!(flags
& TPM_POPUPMENU
);
2797 pPopupMenu
->fIsSysMenu
= !!(flags
& TPM_SYSTEM_MENU
);
2798 pPopupMenu
->fNoNotify
= !!(flags
& TPM_NONOTIFY
);
2799 pPopupMenu
->fRightButton
= !!(flags
& TPM_RIGHTBUTTON
);
2800 pPopupMenu
->fSynchronous
= !!(flags
& TPM_RETURNCMD
);
2802 if (pPopupMenu
->fRightButton
)
2803 pPopupMenu
->fFirstClick
= !!(UserGetKeyState(VK_RBUTTON
) & 0x8000);
2805 pPopupMenu
->fFirstClick
= !!(UserGetKeyState(VK_LBUTTON
) & 0x8000);
2807 if (gpsi
->aiSysMet
[SM_MENUDROPALIGNMENT
] ||
2808 menu
->fFlags
& MNF_RTOL
)
2810 pPopupMenu
->fDroppedLeft
= TRUE
;
2815 /***********************************************************************
2818 * Display a popup menu.
2820 static BOOL FASTCALL
MENU_ShowPopup(PWND pwndOwner
, PMENU menu
, UINT id
, UINT flags
,
2821 INT x
, INT y
, INT xanchor
, INT yanchor
)
2827 USER_REFERENCE_ENTRY Ref
;
2829 TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
2830 pwndOwner
, menu
, id
, x
, y
, xanchor
, yanchor
);
2832 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2834 menu
->rgItems
[menu
->iItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2835 menu
->iItem
= NO_SELECTED_ITEM
;
2838 menu
->dwArrowsOn
= 0;
2839 MENU_PopupMenuCalcSize(menu
, pwndOwner
);
2841 /* adjust popup menu pos so that it fits within the desktop */
2843 width
= menu
->cxMenu
+ UserGetSystemMetrics(SM_CXBORDER
);
2844 height
= menu
->cyMenu
+ UserGetSystemMetrics(SM_CYBORDER
);
2846 /* FIXME: should use item rect */
2849 monitor
= UserMonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
2851 if (flags
& TPM_LAYOUTRTL
)
2852 flags
^= TPM_RIGHTALIGN
;
2854 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
2855 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
2857 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
2858 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
2860 if( x
+ width
> monitor
->rcMonitor
.right
)
2862 if( xanchor
&& x
>= width
- xanchor
)
2863 x
-= width
- xanchor
;
2865 if( x
+ width
> monitor
->rcMonitor
.right
)
2866 x
= monitor
->rcMonitor
.right
- width
;
2868 if( x
< monitor
->rcMonitor
.left
) x
= monitor
->rcMonitor
.left
;
2870 if( y
+ height
> monitor
->rcMonitor
.bottom
)
2872 if( yanchor
&& y
>= height
+ yanchor
)
2873 y
-= height
+ yanchor
;
2875 if( y
+ height
> monitor
->rcMonitor
.bottom
)
2876 y
= monitor
->rcMonitor
.bottom
- height
;
2878 if( y
< monitor
->rcMonitor
.top
) y
= monitor
->rcMonitor
.top
;
2880 pWnd
= ValidateHwndNoErr( menu
->hWnd
);
2884 ERR("menu->hWnd bad hwnd %p\n",menu
->hWnd
);
2889 top_popup
= menu
->hWnd
;
2890 top_popup_hmenu
= UserHMGetHandle(menu
);
2893 /* Display the window */
2894 UserRefObjectCo(pWnd
, &Ref
);
2895 co_WinPosSetWindowPos( pWnd
, HWND_TOPMOST
, x
, y
, width
, height
, SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
2897 co_IntUpdateWindows(pWnd
, RDW_ALLCHILDREN
, FALSE
);
2899 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART
, pWnd
, OBJID_CLIENT
, CHILDID_SELF
, 0);
2900 UserDerefObjectCo(pWnd
);
2905 /***********************************************************************
2906 * MENU_EnsureMenuItemVisible
2908 void MENU_EnsureMenuItemVisible(PMENU lppop
, UINT wIndex
, HDC hdc
)
2910 USER_REFERENCE_ENTRY Ref
;
2911 if (lppop
->dwArrowsOn
)
2913 ITEM
*item
= &lppop
->rgItems
[wIndex
];
2914 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
2915 UINT nOldPos
= lppop
->iTop
;
2917 UINT arrow_bitmap_height
;
2918 PWND pWnd
= ValidateHwndNoErr(lppop
->hWnd
);
2920 IntGetClientRect(pWnd
, &rc
);
2922 arrow_bitmap_height
= gpsi
->oembmi
[OBI_DNARROW
].cy
;
2924 rc
.top
+= arrow_bitmap_height
;
2925 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
2927 nMaxHeight
-= UserGetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
2928 UserRefObjectCo(pWnd
, &Ref
);
2929 if (item
->cyItem
> lppop
->iTop
+ nMaxHeight
)
2931 lppop
->iTop
= item
->cyItem
- nMaxHeight
;
2932 IntScrollWindow(pWnd
, 0, nOldPos
- lppop
->iTop
, &rc
, &rc
);
2933 MENU_DrawScrollArrows(lppop
, hdc
);
2934 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2936 else if (item
->yItem
- MENU_TOP_MARGIN
< lppop
->iTop
)
2938 lppop
->iTop
= item
->yItem
- MENU_TOP_MARGIN
;
2939 IntScrollWindow(pWnd
, 0, nOldPos
- lppop
->iTop
, &rc
, &rc
);
2940 MENU_DrawScrollArrows(lppop
, hdc
);
2941 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2943 UserDerefObjectCo(pWnd
);
2947 /***********************************************************************
2950 static void FASTCALL
MENU_SelectItem(PWND pwndOwner
, PMENU menu
, UINT wIndex
,
2951 BOOL sendMenuSelect
, PMENU topmenu
)
2956 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner
, menu
, wIndex
, sendMenuSelect
);
2958 if (!menu
|| !menu
->cItems
) return;
2960 pWnd
= ValidateHwndNoErr(menu
->hWnd
);
2964 if (menu
->iItem
== wIndex
) return;
2966 if (menu
->fFlags
& MNF_POPUP
)
2967 hdc
= UserGetDCEx(pWnd
, 0, DCX_USESTYLE
);
2969 hdc
= UserGetDCEx(pWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2972 top_popup
= menu
->hWnd
; //pPopupMenu->spwndActivePopup or
2973 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
2974 top_popup_hmenu
= UserHMGetHandle(menu
); //pPopupMenu->spmenu
2977 NtGdiSelectFont( hdc
, ghMenuFont
);
2979 /* Clear previous highlighted item */
2980 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2982 menu
->rgItems
[menu
->iItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2983 MENU_DrawMenuItem(pWnd
, menu
, pwndOwner
, hdc
, &menu
->rgItems
[menu
->iItem
],
2984 menu
->cyMenu
, !(menu
->fFlags
& MNF_POPUP
),
2988 /* Highlight new item (if any) */
2989 menu
->iItem
= wIndex
;
2990 if (menu
->iItem
!= NO_SELECTED_ITEM
)
2992 if (!(menu
->rgItems
[wIndex
].fType
& MF_SEPARATOR
))
2994 menu
->rgItems
[wIndex
].fState
|= MF_HILITE
;
2995 MENU_EnsureMenuItemVisible(menu
, wIndex
, hdc
);
2996 MENU_DrawMenuItem(pWnd
, menu
, pwndOwner
, hdc
,
2997 &menu
->rgItems
[wIndex
], menu
->cyMenu
, !(menu
->fFlags
& MNF_POPUP
), ODA_SELECT
);
3001 ITEM
*ip
= &menu
->rgItems
[menu
->iItem
];
3002 WPARAM wParam
= MAKEWPARAM( ip
->spSubMenu
? wIndex
: ip
->wID
,
3003 ip
->fType
| ip
->fState
|
3004 (ip
->spSubMenu
? MF_POPUP
: 0) |
3005 (menu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0 ) );
3007 co_IntSendMessage(UserHMGetHandle(pwndOwner
), WM_MENUSELECT
, wParam
, (LPARAM
) UserHMGetHandle(menu
));
3010 else if (sendMenuSelect
)
3015 pos
= MENU_FindSubMenu(&topmenu
, menu
);
3016 if (pos
!= NO_SELECTED_ITEM
)
3018 ITEM
*ip
= &topmenu
->rgItems
[pos
];
3019 WPARAM wParam
= MAKEWPARAM( Pos
, ip
->fType
| ip
->fState
|
3020 (ip
->spSubMenu
? MF_POPUP
: 0) |
3021 (topmenu
->fFlags
& MNF_SYSMENU
? MF_SYSMENU
: 0 ) );
3023 co_IntSendMessage(UserHMGetHandle(pwndOwner
), WM_MENUSELECT
, wParam
, (LPARAM
) UserHMGetHandle(topmenu
));
3027 UserReleaseDC(pWnd
, hdc
, FALSE
);
3030 /***********************************************************************
3033 * Moves currently selected item according to the Offset parameter.
3034 * If there is no selection then it should select the last item if
3035 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3037 static void FASTCALL
MENU_MoveSelection(PWND pwndOwner
, PMENU menu
, INT offset
)
3041 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner
, menu
, offset
);
3043 if ((!menu
) || (!menu
->rgItems
)) return;
3045 if ( menu
->iItem
!= NO_SELECTED_ITEM
)
3047 if ( menu
->cItems
== 1 )
3050 for (i
= menu
->iItem
+ offset
; i
>= 0 && i
< menu
->cItems
3052 if (!(menu
->rgItems
[i
].fType
& MF_SEPARATOR
))
3054 MENU_SelectItem( pwndOwner
, menu
, i
, TRUE
, 0 );
3059 for ( i
= (offset
> 0) ? 0 : menu
->cItems
- 1;
3060 i
>= 0 && i
< menu
->cItems
; i
+= offset
)
3061 if (!(menu
->rgItems
[i
].fType
& MF_SEPARATOR
))
3063 MENU_SelectItem( pwndOwner
, menu
, i
, TRUE
, 0 );
3068 /***********************************************************************
3071 * Hide the sub-popup menus of this menu.
3073 static void FASTCALL
MENU_HideSubPopups(PWND pWndOwner
, PMENU Menu
,
3074 BOOL SendMenuSelect
, UINT wFlags
)
3076 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner
, Menu
, SendMenuSelect
);
3078 if ( Menu
&& top_popup
)
3082 if (Menu
->iItem
!= NO_SELECTED_ITEM
)
3084 Item
= &Menu
->rgItems
[Menu
->iItem
];
3085 if (!(Item
->spSubMenu
) ||
3086 !(Item
->fState
& MF_MOUSESELECT
)) return;
3087 Item
->fState
&= ~MF_MOUSESELECT
;
3092 if (Item
->spSubMenu
)
3095 if (!VerifyMenu(Item
->spSubMenu
)) return;
3096 pWnd
= ValidateHwndNoErr(Item
->spSubMenu
->hWnd
);
3097 MENU_HideSubPopups(pWndOwner
, Item
->spSubMenu
, FALSE
, wFlags
);
3098 MENU_SelectItem(pWndOwner
, Item
->spSubMenu
, NO_SELECTED_ITEM
, SendMenuSelect
, NULL
);
3099 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu
,pWndOwner
->IDMenu
);
3100 co_UserDestroyWindow(pWnd
);
3102 /* Native returns handle to destroyed window */
3103 if (!(wFlags
& TPM_NONOTIFY
))
3105 co_IntSendMessage( UserHMGetHandle(pWndOwner
), WM_UNINITMENUPOPUP
, (WPARAM
)UserHMGetHandle(Item
->spSubMenu
),
3106 MAKELPARAM(0, IS_SYSTEM_MENU(Item
->spSubMenu
)) );
3109 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3110 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3112 Item
->spSubMenu
->hWnd
= NULL
;
3118 /***********************************************************************
3121 * Display the sub-menu of the selected item of this menu.
3122 * Return the handle of the