4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
63 WINE_DECLARE_DEBUG_CHANNEL(accel
);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType
; /* Item type. */
74 UINT fState
; /* Item state. */
75 UINT_PTR wID
; /* Item id. */
76 HMENU hSubMenu
; /* Pop-up menu. */
77 HBITMAP hCheckBit
; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
79 LPWSTR text
; /* Item text. */
80 ULONG_PTR dwItemData
; /* Application defined. */
81 LPWSTR dwTypeData
; /* depends on fMask */
82 HBITMAP hbmpItem
; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect
; /* Item area (relative to menu window) */
85 UINT xTab
; /* X position of text after Tab */
86 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
90 /* Popup menu structure */
92 struct user_object obj
;
93 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width
; /* Width of the whole menu */
95 WORD Height
; /* Height of the whole menu */
96 UINT nItems
; /* Number of items in the menu */
97 HWND hWnd
; /* Window containing the menu */
98 MENUITEM
*items
; /* Array of menu items */
99 UINT FocusedItem
; /* Currently focused item */
100 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling
; /* Scroll arrows are active */
103 UINT nScrollPos
; /* Current scroll position */
104 UINT nTotalHeight
; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle
; /* Extended menu style */
107 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack
; /* brush for menu background */
109 DWORD dwContextHelpID
;
110 DWORD dwMenuData
; /* application defined value */
111 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
112 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU
, *LPPOPUPMENU
;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
124 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu
; /* initial menu */
126 HWND hOwnerWnd
; /* where notifications are sent */
130 #define MENU_MAGIC 0x554d /* 'MU' */
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize
;
176 static UINT ODitemheight
; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup
;
181 static HMENU top_popup_hmenu
;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu
= FALSE
;
186 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
188 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class
=
195 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
196 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
197 WINPROC_MENU
, /* proc */
198 sizeof(HMENU
), /* extra */
199 IDC_ARROW
, /* cursor */
200 (HBRUSH
)(COLOR_MENU
+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
221 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
224 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix
);
230 UINT flags
= mp
->fType
;
231 TRACE( "{ ID=0x%lx", mp
->wID
);
233 TRACE( ", Sub=%p", mp
->hSubMenu
);
237 MENUFLAG( MFT_SEPARATOR
, "sep");
238 MENUFLAG( MFT_OWNERDRAW
, "own");
239 MENUFLAG( MFT_BITMAP
, "bit");
240 MENUFLAG(MF_POPUP
, "pop");
241 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
242 MENUFLAG(MFT_MENUBREAK
, "brk");
243 MENUFLAG(MFT_RADIOCHECK
, "radio");
244 MENUFLAG(MFT_RIGHTORDER
, "rorder");
245 MENUFLAG(MF_SYSMENU
, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
248 TRACE( "+0x%x", flags
);
254 MENUFLAG(MFS_GRAYED
, "grey");
255 MENUFLAG(MFS_DEFAULT
, "default");
256 MENUFLAG(MFS_DISABLED
, "dis");
257 MENUFLAG(MFS_CHECKED
, "check");
258 MENUFLAG(MFS_HILITE
, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
260 MENUFLAG(MF_MOUSESELECT
, "mouse");
262 TRACE( "+0x%x", flags
);
265 TRACE( ", Chk=%p", mp
->hCheckBit
);
267 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
269 TRACE( ", Text=%s", debugstr_w(mp
->text
));
271 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
274 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
275 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
277 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
282 TRACE(" %s\n", postfix
);
289 /***********************************************************************
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
296 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
298 if (menu
== OBJ_OTHER_PROCESS
)
300 WARN( "other process menu %p?\n", hMenu
);
303 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu
);
308 /***********************************************************************
311 * Get the system menu of a window
313 static HMENU
get_win_sys_menu( HWND hwnd
)
316 WND
*win
= WIN_GetPtr( hwnd
);
317 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
320 WIN_ReleasePtr( win
);
325 /***********************************************************************
328 static HFONT
get_menu_font( BOOL bold
)
330 static HFONT hMenuFont
, hMenuFontBold
;
332 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
336 NONCLIENTMETRICSW ncm
;
339 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
344 ncm
.lfMenuFont
.lfWeight
+= 300;
345 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
347 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
348 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
352 /* another thread beat us to it */
360 /***********************************************************************
363 static HBITMAP
get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap
;
367 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP
get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap
;
378 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP
get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap
;
389 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP
get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap
;
400 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP
get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap
;
411 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
415 /***********************************************************************
418 * Return the default system menu.
420 static HMENU
MENU_CopySysPopup(void)
422 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
423 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
427 MENUITEMINFOW miteminfo
;
428 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
429 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
430 /* decorate the menu with bitmaps */
431 minfo
.cbSize
= sizeof( MENUINFO
);
432 minfo
.dwStyle
= MNS_CHECKORBMP
;
433 minfo
.fMask
= MIM_STYLE
;
434 SetMenuInfo( hMenu
, &minfo
);
435 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
436 miteminfo
.fMask
= MIIM_BITMAP
;
437 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
438 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
439 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
440 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
441 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
442 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
443 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
444 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
445 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
448 ERR("Unable to load default system menu\n" );
450 TRACE("returning %p.\n", hMenu
);
456 /**********************************************************************
459 * Create a copy of the system menu. System menu in Windows is
460 * a special menu bar with the single entry - system menu popup.
461 * This popup is presented to the outside world as a "system menu".
462 * However, the real system menu handle is sometimes seen in the
463 * WM_MENUSELECT parameters (and Word 6 likes it this way).
465 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
469 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
470 if ((hMenu
= CreateMenu()))
472 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
473 menu
->wFlags
= MF_SYSMENU
;
474 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
475 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
478 hPopupMenu
= MENU_CopySysPopup();
482 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
483 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
485 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
486 (UINT_PTR
)hPopupMenu
, NULL
);
488 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
489 menu
->items
[0].fState
= 0;
490 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
492 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
495 DestroyMenu( hMenu
);
497 ERR("failed to load system menu!\n");
502 /***********************************************************************
503 * MENU_InitSysMenuPopup
505 * Grey the appropriate items in System menu.
507 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
511 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
512 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
513 gray
= ((style
& WS_MAXIMIZE
) != 0);
514 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
515 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
516 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
517 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
518 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
519 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
520 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
521 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
523 /* The menu item must keep its state if it's disabled */
525 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
529 /******************************************************************************
531 * UINT MENU_GetStartOfNextColumn(
534 *****************************************************************************/
536 static UINT
MENU_GetStartOfNextColumn(
539 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
543 return NO_SELECTED_ITEM
;
545 i
= menu
->FocusedItem
+ 1;
546 if( i
== NO_SELECTED_ITEM
)
549 for( ; i
< menu
->nItems
; ++i
) {
550 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
554 return NO_SELECTED_ITEM
;
558 /******************************************************************************
560 * UINT MENU_GetStartOfPrevColumn(
563 *****************************************************************************/
565 static UINT
MENU_GetStartOfPrevColumn(
568 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
572 return NO_SELECTED_ITEM
;
574 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
575 return NO_SELECTED_ITEM
;
577 /* Find the start of the column */
579 for(i
= menu
->FocusedItem
; i
!= 0 &&
580 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
584 return NO_SELECTED_ITEM
;
586 for(--i
; i
!= 0; --i
) {
587 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
591 TRACE("ret %d.\n", i
);
598 /***********************************************************************
601 * Find a menu item. Return a pointer on the item, and modifies *hmenu
602 * in case the item was in a sub-menu.
604 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
607 MENUITEM
*fallback
= NULL
;
608 UINT fallback_pos
= 0;
611 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
612 if (wFlags
& MF_BYPOSITION
)
614 if (*nPos
>= menu
->nItems
) return NULL
;
615 return &menu
->items
[*nPos
];
619 MENUITEM
*item
= menu
->items
;
620 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
622 if (item
->fType
& MF_POPUP
)
624 HMENU hsubmenu
= item
->hSubMenu
;
625 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
631 else if (item
->wID
== *nPos
)
633 /* fallback to this item if nothing else found */
638 else if (item
->wID
== *nPos
)
647 *nPos
= fallback_pos
;
652 /***********************************************************************
655 * Find a Sub menu. Return the position of the submenu, and modifies
656 * *hmenu in case it is found in another sub-menu.
657 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
659 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
664 if (((*hmenu
)==(HMENU
)0xffff) ||
665 (!(menu
= MENU_GetMenu(*hmenu
))))
666 return NO_SELECTED_ITEM
;
668 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
669 if(!(item
->fType
& MF_POPUP
)) continue;
670 if (item
->hSubMenu
== hSubTarget
) {
674 HMENU hsubmenu
= item
->hSubMenu
;
675 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
676 if (pos
!= NO_SELECTED_ITEM
) {
682 return NO_SELECTED_ITEM
;
685 /***********************************************************************
688 static void MENU_FreeItemData( MENUITEM
* item
)
691 HeapFree( GetProcessHeap(), 0, item
->text
);
694 /***********************************************************************
695 * MENU_AdjustMenuItemRect
697 * Adjust menu item rectangle according to scrolling state.
700 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
702 if (menu
->bScrolling
)
704 UINT arrow_bitmap_height
;
707 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
708 arrow_bitmap_height
= bmp
.bmHeight
;
709 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
710 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
715 /***********************************************************************
716 * MENU_FindItemByCoords
718 * Find the item at the specified coordinates (screen coords). Does
719 * not work for child windows and therefore should not be called for
720 * an arbitrary system menu.
722 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
723 POINT pt
, UINT
*pos
)
729 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
733 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
736 MENU_AdjustMenuItemRect(menu
, &rect
);
737 if (PtInRect(&rect
, pt
))
747 /***********************************************************************
750 * Find the menu item selected by a key press.
751 * Return item id, -1 if none, -2 if we should close the menu.
753 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
754 WCHAR key
, BOOL forceMenuChar
)
756 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
758 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
762 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
763 MENUITEM
*item
= menu
->items
;
770 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
774 WCHAR
*p
= item
->text
- 2;
777 p
= strchrW (p
+ 2, '&');
779 while (p
!= NULL
&& p
[1] == '&');
780 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
784 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
785 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
786 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
787 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
793 /***********************************************************************
794 * MENU_GetBitmapItemSize
796 * Get the size of a bitmap item.
798 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
802 HBITMAP bmp
= lpitem
->hbmpItem
;
804 size
->cx
= size
->cy
= 0;
806 /* check if there is a magic menu item associated with this item */
807 switch( (INT_PTR
) bmp
)
809 case (INT_PTR
)HBMMENU_CALLBACK
:
811 MEASUREITEMSTRUCT measItem
;
812 measItem
.CtlType
= ODT_MENU
;
814 measItem
.itemID
= lpitem
->wID
;
815 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
816 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
817 measItem
.itemData
= lpitem
->dwItemData
;
818 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
819 size
->cx
= measItem
.itemWidth
;
820 size
->cy
= measItem
.itemHeight
;
824 case (INT_PTR
)HBMMENU_SYSTEM
:
825 if (lpitem
->dwItemData
)
827 bmp
= (HBITMAP
)lpitem
->dwItemData
;
831 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
832 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
833 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
834 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
835 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
836 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
839 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
840 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
841 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
842 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
843 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
844 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
847 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
849 size
->cx
= bm
.bmWidth
;
850 size
->cy
= bm
.bmHeight
;
854 /***********************************************************************
855 * MENU_DrawBitmapItem
857 * Draw a bitmap item.
859 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
860 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
866 int w
= rect
->right
- rect
->left
;
867 int h
= rect
->bottom
- rect
->top
;
870 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
873 /* Check if there is a magic menu item associated with this item */
874 if (IS_MAGIC_BITMAP(hbmToDraw
))
880 switch((INT_PTR
)hbmToDraw
)
882 case (INT_PTR
)HBMMENU_SYSTEM
:
883 if (lpitem
->dwItemData
)
885 bmp
= (HBITMAP
)lpitem
->dwItemData
;
886 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
890 static HBITMAP hBmpSysMenu
;
892 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
894 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
895 /* only use right half of the bitmap */
896 bmp_xoffset
= bm
.bmWidth
/ 2;
897 bm
.bmWidth
-= bmp_xoffset
;
900 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
901 flags
= DFCS_CAPTIONRESTORE
;
903 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
904 flags
= DFCS_CAPTIONMIN
;
906 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
907 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
909 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
910 flags
= DFCS_CAPTIONCLOSE
;
912 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
913 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
915 case (INT_PTR
)HBMMENU_CALLBACK
:
917 DRAWITEMSTRUCT drawItem
;
918 drawItem
.CtlType
= ODT_MENU
;
920 drawItem
.itemID
= lpitem
->wID
;
921 drawItem
.itemAction
= odaction
;
922 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
923 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
924 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
925 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
926 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
927 drawItem
.hwndItem
= (HWND
)hmenu
;
929 drawItem
.itemData
= lpitem
->dwItemData
;
930 drawItem
.rcItem
= *rect
;
931 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
935 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
938 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
941 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
944 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
948 FIXME("Magic %p not implemented\n", hbmToDraw
);
953 /* draw the magic bitmaps using marlett font characters */
954 /* FIXME: fontsize and the position (x,y) could probably be better */
955 HFONT hfont
, hfontsav
;
956 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
957 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
958 { 'M','a','r','l','e','t','t',0 } };
959 logfont
.lfHeight
= min( h
, w
) - 5 ;
960 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
961 hfont
= CreateFontIndirectW( &logfont
);
962 hfontsav
= SelectObject(hdc
, hfont
);
963 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
964 SelectObject(hdc
, hfontsav
);
965 DeleteObject( hfont
);
970 InflateRect( &r
, -1, -1 );
971 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
972 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
977 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
980 hdcMem
= CreateCompatibleDC( hdc
);
981 SelectObject( hdcMem
, bmp
);
983 /* handle fontsize > bitmap_height */
984 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
986 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
987 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
988 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
989 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
994 /***********************************************************************
997 * Calculate the size of the menu item and store it in lpitem->rect.
999 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1000 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1003 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1004 UINT arrow_bitmap_width
;
1008 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1009 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1010 (menuBar
? " (MenuBar)" : ""));
1012 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1013 arrow_bitmap_width
= bm
.bmWidth
;
1015 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1016 if( !menucharsize
.cx
) {
1017 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1018 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1019 * but it is unlikely an application will depend on that */
1020 ODitemheight
= HIWORD( GetDialogBaseUnits());
1023 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1025 if (lpitem
->fType
& MF_OWNERDRAW
)
1027 MEASUREITEMSTRUCT mis
;
1028 mis
.CtlType
= ODT_MENU
;
1030 mis
.itemID
= lpitem
->wID
;
1031 mis
.itemData
= lpitem
->dwItemData
;
1032 mis
.itemHeight
= ODitemheight
;
1034 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1035 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1036 * width of a menufont character to the width of an owner-drawn menu.
1038 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1040 /* under at least win95 you seem to be given a standard
1041 height for the menu and the height value is ignored */
1042 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1044 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1046 TRACE("id=%04lx size=%dx%d\n",
1047 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1048 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1052 if (lpitem
->fType
& MF_SEPARATOR
)
1054 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1056 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1064 if (lpitem
->hbmpItem
) {
1067 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1068 /* Keep the size of the bitmap in callback mode to be able
1069 * to draw it correctly */
1070 lpitem
->bmpsize
= size
;
1071 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1072 lpitem
->rect
.right
+= size
.cx
+ 2;
1073 itemheight
= size
.cy
+ 2;
1075 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1076 lpitem
->rect
.right
+= check_bitmap_width
;
1077 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1078 lpitem
->xTab
= lpitem
->rect
.right
;
1079 lpitem
->rect
.right
+= arrow_bitmap_width
;
1080 } else if (lpitem
->hbmpItem
) { /* menuBar */
1083 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1084 lpitem
->bmpsize
= size
;
1085 lpitem
->rect
.right
+= size
.cx
;
1086 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1087 itemheight
= size
.cy
;
1090 /* it must be a text item - unless it's the system menu */
1091 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1092 HFONT hfontOld
= NULL
;
1093 RECT rc
= lpitem
->rect
;
1094 LONG txtheight
, txtwidth
;
1096 if ( lpitem
->fState
& MFS_DEFAULT
) {
1097 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1100 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1101 DT_SINGLELINE
|DT_CALCRECT
);
1102 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1103 itemheight
= max( max( itemheight
, txtheight
),
1104 GetSystemMetrics( SM_CYMENU
) - 1);
1105 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1107 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1110 int n
= (int)( p
- lpitem
->text
);
1111 /* Item contains a tab (only meaningful in popup menus) */
1112 /* get text size before the tab */
1113 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1114 DT_SINGLELINE
|DT_CALCRECT
);
1115 txtwidth
= rc
.right
- rc
.left
;
1116 p
+= 1; /* advance past the Tab */
1117 /* get text size after the tab */
1118 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1119 DT_SINGLELINE
|DT_CALCRECT
);
1120 lpitem
->xTab
+= txtwidth
;
1121 txtheight
= max( txtheight
, tmpheight
);
1122 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1123 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1125 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1126 DT_SINGLELINE
|DT_CALCRECT
);
1127 txtwidth
= rc
.right
- rc
.left
;
1128 lpitem
->xTab
+= txtwidth
;
1130 lpitem
->rect
.right
+= 2 + txtwidth
;
1131 itemheight
= max( itemheight
,
1132 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1134 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1135 } else if( menuBar
) {
1136 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1138 lpitem
->rect
.bottom
+= itemheight
;
1139 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1143 /***********************************************************************
1144 * MENU_GetMaxPopupHeight
1147 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1150 return lppop
->cyMax
;
1151 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1155 /***********************************************************************
1156 * MENU_PopupMenuCalcSize
1158 * Calculate the size of a popup menu.
1160 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1165 int textandbmp
= FALSE
;
1166 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1168 lppop
->Width
= lppop
->Height
= 0;
1169 if (lppop
->nItems
== 0) return;
1172 SelectObject( hdc
, get_menu_font(FALSE
));
1177 lppop
->textOffset
= 0;
1179 while (start
< lppop
->nItems
)
1181 lpitem
= &lppop
->items
[start
];
1183 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1184 orgX
+= MENU_COL_SPACE
;
1185 orgY
= MENU_TOP_MARGIN
;
1187 maxTab
= maxTabWidth
= 0;
1188 /* Parse items until column break or end of menu */
1189 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1192 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1194 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1195 maxX
= max( maxX
, lpitem
->rect
.right
);
1196 orgY
= lpitem
->rect
.bottom
;
1197 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1199 maxTab
= max( maxTab
, lpitem
->xTab
);
1200 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1202 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1205 /* Finish the column (set all items to the largest width found) */
1206 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1207 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1209 lpitem
->rect
.right
= maxX
;
1210 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1211 lpitem
->xTab
= maxTab
;
1214 lppop
->Height
= max( lppop
->Height
, orgY
);
1217 lppop
->Width
= maxX
;
1218 /* if none of the items have both text and bitmap then
1219 * the text and bitmaps are all aligned on the left. If there is at
1220 * least one item with both text and bitmap then bitmaps are
1221 * on the left and texts left aligned with the right hand side
1223 if( !textandbmp
) lppop
->textOffset
= 0;
1225 /* space for 3d border */
1226 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1229 /* Adjust popup height if it exceeds maximum */
1230 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1231 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1232 if (lppop
->Height
>= maxHeight
)
1234 lppop
->Height
= maxHeight
;
1235 lppop
->bScrolling
= TRUE
;
1239 lppop
->bScrolling
= FALSE
;
1242 ReleaseDC( 0, hdc
);
1246 /***********************************************************************
1247 * MENU_MenuBarCalcSize
1249 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1250 * height is off by 1 pixel which causes lengthy window relocations when
1251 * active document window is maximized/restored.
1253 * Calculate the size of the menu bar.
1255 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1256 LPPOPUPMENU lppop
, HWND hwndOwner
)
1259 UINT start
, i
, helpPos
;
1260 int orgX
, orgY
, maxY
;
1262 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1263 if (lppop
->nItems
== 0) return;
1264 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1265 lppop
->Width
= lprect
->right
- lprect
->left
;
1267 maxY
= lprect
->top
+1;
1270 lppop
->textOffset
= 0;
1271 while (start
< lppop
->nItems
)
1273 lpitem
= &lppop
->items
[start
];
1274 orgX
= lprect
->left
;
1277 /* Parse items until line break or end of menu */
1278 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1280 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1282 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1284 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1285 debug_print_menuitem (" item: ", lpitem
, "");
1286 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1288 if (lpitem
->rect
.right
> lprect
->right
)
1290 if (i
!= start
) break;
1291 else lpitem
->rect
.right
= lprect
->right
;
1293 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1294 orgX
= lpitem
->rect
.right
;
1297 /* Finish the line (set all items to the largest height found) */
1298 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1301 lprect
->bottom
= maxY
;
1302 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1304 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1305 /* the last item (if several lines, only move the last line) */
1306 if (helpPos
== ~0U) return;
1307 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1308 orgY
= lpitem
->rect
.top
;
1309 orgX
= lprect
->right
;
1310 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1311 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1312 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1313 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1314 lpitem
->rect
.right
= orgX
;
1315 orgX
= lpitem
->rect
.left
;
1320 /***********************************************************************
1321 * MENU_DrawScrollArrows
1323 * Draw scroll arrows.
1326 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1328 HDC hdcMem
= CreateCompatibleDC(hdc
);
1329 HBITMAP hOrigBitmap
;
1330 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1334 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1335 arrow_bitmap_width
= bmp
.bmWidth
;
1336 arrow_bitmap_height
= bmp
.bmHeight
;
1339 if (lppop
->nScrollPos
)
1340 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1342 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1345 rect
.right
= lppop
->Width
;
1346 rect
.bottom
= arrow_bitmap_height
;
1347 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1348 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1349 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1350 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1351 rect
.bottom
= lppop
->Height
;
1352 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1353 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1354 SelectObject(hdcMem
, get_down_arrow_bitmap());
1356 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1357 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1358 lppop
->Height
- arrow_bitmap_height
,
1359 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1360 SelectObject(hdcMem
, hOrigBitmap
);
1365 /***********************************************************************
1368 * Draws the popup-menu arrow.
1370 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1371 UINT arrow_bitmap_height
)
1373 HDC hdcMem
= CreateCompatibleDC( hdc
);
1374 HBITMAP hOrigBitmap
;
1376 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1377 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1378 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1379 arrow_bitmap_width
, arrow_bitmap_height
,
1380 hdcMem
, 0, 0, SRCCOPY
);
1381 SelectObject( hdcMem
, hOrigBitmap
);
1384 /***********************************************************************
1387 * Draw a single menu item.
1389 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1390 UINT height
, BOOL menuBar
, UINT odaction
)
1393 BOOL flat_menu
= FALSE
;
1395 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1396 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1399 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1403 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1404 arrow_bitmap_width
= bmp
.bmWidth
;
1405 arrow_bitmap_height
= bmp
.bmHeight
;
1408 if (lpitem
->fType
& MF_SYSMENU
)
1410 if( !IsIconic(hwnd
) )
1411 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1415 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1416 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1420 if (lpitem
->fState
& MF_HILITE
)
1422 if(menuBar
&& !flat_menu
) {
1423 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1424 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1426 if(lpitem
->fState
& MF_GRAYED
)
1427 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1429 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1430 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1435 if (lpitem
->fState
& MF_GRAYED
)
1436 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1438 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1439 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1442 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1443 rect
= lpitem
->rect
;
1444 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1446 if (lpitem
->fType
& MF_OWNERDRAW
)
1449 ** Experimentation under Windows reveals that an owner-drawn
1450 ** menu is given the rectangle which includes the space it requested
1451 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1452 ** and a popup-menu arrow. This is the value of lpitem->rect.
1453 ** Windows will leave all drawing to the application except for
1454 ** the popup-menu arrow. Windows always draws that itself, after
1455 ** the menu owner has finished drawing.
1459 dis
.CtlType
= ODT_MENU
;
1461 dis
.itemID
= lpitem
->wID
;
1462 dis
.itemData
= lpitem
->dwItemData
;
1464 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1465 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1466 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1467 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1468 dis
.hwndItem
= (HWND
)hmenu
;
1471 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1472 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1473 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1474 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1475 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1476 /* Draw the popup-menu arrow */
1477 if (lpitem
->fType
& MF_POPUP
)
1478 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1479 arrow_bitmap_height
);
1483 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1485 if (lpitem
->fState
& MF_HILITE
)
1489 InflateRect (&rect
, -1, -1);
1490 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1491 InflateRect (&rect
, 1, 1);
1492 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1497 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1499 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1503 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1505 SetBkMode( hdc
, TRANSPARENT
);
1507 /* vertical separator */
1508 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1513 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1515 rc
.bottom
= height
- 3;
1518 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1519 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1520 LineTo( hdc
, rc
.left
, rc
.bottom
);
1521 SelectObject( hdc
, oldPen
);
1524 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1527 /* horizontal separator */
1528 if (lpitem
->fType
& MF_SEPARATOR
)
1535 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1538 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1539 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1540 LineTo( hdc
, rc
.right
, rc
.top
);
1541 SelectObject( hdc
, oldPen
);
1544 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1548 /* helper lines for debugging */
1549 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1550 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1551 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1552 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1555 if (lpitem
->hbmpItem
) {
1556 /* calculate the bitmap rectangle in coordinates relative
1557 * to the item rectangle */
1559 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1562 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1564 else if (menu
->dwStyle
& MNS_NOCHECK
)
1566 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1569 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1570 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1571 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1574 bmprc
.top
= (rect
.bottom
- rect
.top
-
1575 lpitem
->bmpsize
.cy
) / 2;
1576 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1582 INT y
= rect
.top
+ rect
.bottom
;
1584 int checked
= FALSE
;
1585 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1586 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1587 /* Draw the check mark
1590 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1592 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1593 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1594 lpitem
->hUnCheckBit
;
1595 if (bm
) /* we have a custom bitmap */
1597 HDC hdcMem
= CreateCompatibleDC( hdc
);
1599 SelectObject( hdcMem
, bm
);
1600 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1601 check_bitmap_width
, check_bitmap_height
,
1602 hdcMem
, 0, 0, SRCCOPY
);
1606 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1609 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1610 check_bitmap_height
, 1, 1, NULL
);
1611 HDC hdcMem
= CreateCompatibleDC( hdc
);
1613 SelectObject( hdcMem
, bm
);
1614 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1615 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1616 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1617 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1618 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1619 hdcMem
, 0, 0, SRCCOPY
);
1625 if( lpitem
->hbmpItem
&&
1626 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1628 /* some applications make this assumption on the DC's origin */
1629 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1630 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1632 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1634 /* Draw the popup-menu arrow */
1635 if (lpitem
->fType
& MF_POPUP
)
1636 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1637 arrow_bitmap_height
);
1639 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1640 rect
.left
+= check_bitmap_width
;
1641 rect
.right
-= arrow_bitmap_width
;
1643 else if( lpitem
->hbmpItem
)
1644 { /* Draw the bitmap */
1647 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1648 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1650 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1652 /* process text if present */
1658 UINT uFormat
= (menuBar
) ?
1659 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1660 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1662 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1663 rect
.left
+= menu
->textOffset
;
1665 if ( lpitem
->fState
& MFS_DEFAULT
)
1667 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1671 if( lpitem
->hbmpItem
)
1672 rect
.left
+= lpitem
->bmpsize
.cx
;
1673 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1674 rect
.left
+= menucharsize
.cx
;
1675 rect
.right
-= menucharsize
.cx
;
1678 for (i
= 0; lpitem
->text
[i
]; i
++)
1679 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1682 if(lpitem
->fState
& MF_GRAYED
)
1684 if (!(lpitem
->fState
& MF_HILITE
) )
1686 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1687 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1688 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1689 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1691 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1694 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1696 /* paint the shortcut text */
1697 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1699 if (lpitem
->text
[i
] == '\t')
1701 rect
.left
= lpitem
->xTab
;
1702 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1706 rect
.right
= lpitem
->xTab
;
1707 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1710 if(lpitem
->fState
& MF_GRAYED
)
1712 if (!(lpitem
->fState
& MF_HILITE
) )
1714 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1715 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1716 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1717 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1719 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1721 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1725 SelectObject (hdc
, hfontOld
);
1730 /***********************************************************************
1731 * MENU_DrawPopupMenu
1733 * Paint a popup menu.
1735 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1737 HBRUSH hPrevBrush
= 0;
1740 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1742 GetClientRect( hwnd
, &rect
);
1744 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1745 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1749 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1751 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1755 BOOL flat_menu
= FALSE
;
1757 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1759 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1761 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1763 if( (menu
= MENU_GetMenu( hmenu
)))
1765 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1766 /* draw menu items */
1773 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1774 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1775 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1777 /* draw scroll arrows */
1778 if (menu
->bScrolling
)
1779 MENU_DrawScrollArrows(menu
, hdc
);
1783 SelectObject( hdc
, hPrevBrush
);
1788 /***********************************************************************
1791 * Paint a menu bar. Returns the height of the menu bar.
1792 * called from [windows/nonclient.c]
1794 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1799 HMENU hMenu
= GetMenu(hwnd
);
1801 lppop
= MENU_GetMenu( hMenu
);
1802 if (lppop
== NULL
|| lprect
== NULL
)
1804 return GetSystemMetrics(SM_CYMENU
);
1809 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1811 if (lppop
->Height
== 0)
1812 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1814 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1816 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1817 return lppop
->Height
;
1820 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1824 /***********************************************************************
1827 * Display a popup menu.
1829 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1830 INT x
, INT y
, INT xanchor
, INT yanchor
)
1838 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1839 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1841 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1842 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1844 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1845 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1848 /* store the owner for DrawItem */
1849 menu
->hwndOwner
= hwndOwner
;
1851 menu
->nScrollPos
= 0;
1852 MENU_PopupMenuCalcSize( menu
);
1854 /* adjust popup menu pos so that it fits within the desktop */
1856 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1857 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1859 /* FIXME: should use item rect */
1862 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1863 info
.cbSize
= sizeof(info
);
1864 GetMonitorInfoW( monitor
, &info
);
1866 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1867 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1869 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1870 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1872 if( x
+ width
> info
.rcWork
.right
)
1874 if( xanchor
&& x
>= width
- xanchor
)
1875 x
-= width
- xanchor
;
1877 if( x
+ width
> info
.rcWork
.right
)
1878 x
= info
.rcWork
.right
- width
;
1880 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1882 if( y
+ height
> info
.rcWork
.bottom
)
1884 if( yanchor
&& y
>= height
+ yanchor
)
1885 y
-= height
+ yanchor
;
1887 if( y
+ height
> info
.rcWork
.bottom
)
1888 y
= info
.rcWork
.bottom
- height
;
1890 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1892 /* NOTE: In Windows, top menu popup is not owned. */
1893 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1894 WS_POPUP
, x
, y
, width
, height
,
1895 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1897 if( !menu
->hWnd
) return FALSE
;
1899 top_popup
= menu
->hWnd
;
1900 top_popup_hmenu
= hmenu
;
1902 /* Display the window */
1904 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1905 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1906 UpdateWindow( menu
->hWnd
);
1911 /***********************************************************************
1912 * MENU_EnsureMenuItemVisible
1915 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1917 if (lppop
->bScrolling
)
1919 MENUITEM
*item
= &lppop
->items
[wIndex
];
1920 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1921 UINT nOldPos
= lppop
->nScrollPos
;
1923 UINT arrow_bitmap_height
;
1926 GetClientRect(lppop
->hWnd
, &rc
);
1928 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1929 arrow_bitmap_height
= bmp
.bmHeight
;
1931 rc
.top
+= arrow_bitmap_height
;
1932 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1934 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1935 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1938 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1939 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1940 MENU_DrawScrollArrows(lppop
, hdc
);
1942 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1944 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1945 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1946 MENU_DrawScrollArrows(lppop
, hdc
);
1952 /***********************************************************************
1955 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1956 BOOL sendMenuSelect
, HMENU topmenu
)
1961 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1963 lppop
= MENU_GetMenu( hmenu
);
1964 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1966 if (lppop
->FocusedItem
== wIndex
) return;
1967 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1968 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1970 top_popup
= lppop
->hWnd
;
1971 top_popup_hmenu
= hmenu
;
1974 SelectObject( hdc
, get_menu_font(FALSE
));
1976 /* Clear previous highlighted item */
1977 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1979 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1980 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1981 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1985 /* Highlight new item (if any) */
1986 lppop
->FocusedItem
= wIndex
;
1987 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1989 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1990 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1991 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1992 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1993 &lppop
->items
[wIndex
], lppop
->Height
,
1994 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1998 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1999 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2000 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2001 ip
->fType
| ip
->fState
|
2002 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2005 else if (sendMenuSelect
) {
2008 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2009 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2010 MENUITEM
*ip
= &ptm
->items
[pos
];
2011 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2012 ip
->fType
| ip
->fState
|
2013 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2017 ReleaseDC( lppop
->hWnd
, hdc
);
2021 /***********************************************************************
2022 * MENU_MoveSelection
2024 * Moves currently selected item according to the offset parameter.
2025 * If there is no selection then it should select the last item if
2026 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2028 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2033 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2035 menu
= MENU_GetMenu( hmenu
);
2036 if ((!menu
) || (!menu
->items
)) return;
2038 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2040 if( menu
->nItems
== 1 ) return; else
2041 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2043 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2045 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2050 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2051 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2052 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2054 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2060 /**********************************************************************
2063 * Insert (allocate) a new item into a menu.
2065 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2070 if (!(menu
= MENU_GetMenu(hMenu
)))
2073 /* Find where to insert new item */
2075 if (flags
& MF_BYPOSITION
) {
2076 if (pos
> menu
->nItems
)
2079 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2082 if (!(menu
= MENU_GetMenu( hMenu
)))
2087 /* Make sure that MDI system buttons stay on the right side.
2088 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2089 * regardless of their id.
2091 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2092 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2095 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2097 /* Create new items array */
2099 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2102 WARN("allocation failed\n" );
2105 if (menu
->nItems
> 0)
2107 /* Copy the old array into the new one */
2108 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2109 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2110 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2111 HeapFree( GetProcessHeap(), 0, menu
->items
);
2113 menu
->items
= newItems
;
2115 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2116 menu
->Height
= 0; /* force size recalculate */
2117 return &newItems
[pos
];
2121 /**********************************************************************
2122 * MENU_ParseResource
2124 * Parse a standard menu resource and add items to the menu.
2125 * Return a pointer to the end of the resource.
2127 * NOTE: flags is equivalent to the mtOption field
2129 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2137 flags
= GET_WORD(res
);
2138 end_flag
= flags
& MF_END
;
2139 /* Remove MF_END because it has the same value as MF_HILITE */
2141 res
+= sizeof(WORD
);
2142 if (!(flags
& MF_POPUP
))
2145 res
+= sizeof(WORD
);
2148 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2149 if (flags
& MF_POPUP
)
2151 HMENU hSubMenu
= CreatePopupMenu();
2152 if (!hSubMenu
) return NULL
;
2153 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2154 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2156 else /* Not a popup */
2158 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2160 } while (!end_flag
);
2165 /**********************************************************************
2166 * MENUEX_ParseResource
2168 * Parse an extended menu resource and add items to the menu.
2169 * Return a pointer to the end of the resource.
2171 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2177 mii
.cbSize
= sizeof(mii
);
2178 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2179 mii
.fType
= GET_DWORD(res
);
2180 res
+= sizeof(DWORD
);
2181 mii
.fState
= GET_DWORD(res
);
2182 res
+= sizeof(DWORD
);
2183 mii
.wID
= GET_DWORD(res
);
2184 res
+= sizeof(DWORD
);
2185 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2186 res
+= sizeof(WORD
);
2187 /* Align the text on a word boundary. */
2188 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2189 mii
.dwTypeData
= (LPWSTR
) res
;
2190 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2191 /* Align the following fields on a dword boundary. */
2192 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2194 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2195 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2197 if (resinfo
& 1) { /* Pop-up? */
2198 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2199 res
+= sizeof(DWORD
);
2200 mii
.hSubMenu
= CreatePopupMenu();
2203 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2204 DestroyMenu(mii
.hSubMenu
);
2207 mii
.fMask
|= MIIM_SUBMENU
;
2208 mii
.fType
|= MF_POPUP
;
2210 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2212 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2213 mii
.wID
, mii
.fType
);
2214 mii
.fType
|= MF_SEPARATOR
;
2216 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2217 } while (!(resinfo
& MF_END
));
2222 /***********************************************************************
2225 * Return the handle of the selected sub-popup menu (if any).
2227 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2232 menu
= MENU_GetMenu( hmenu
);
2234 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2236 item
= &menu
->items
[menu
->FocusedItem
];
2237 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2238 return item
->hSubMenu
;
2243 /***********************************************************************
2244 * MENU_HideSubPopups
2246 * Hide the sub-popup menus of this menu.
2248 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2249 BOOL sendMenuSelect
, UINT wFlags
)
2251 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2253 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2255 if (menu
&& top_popup
)
2261 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2263 item
= &menu
->items
[menu
->FocusedItem
];
2264 if (!(item
->fType
& MF_POPUP
) ||
2265 !(item
->fState
& MF_MOUSESELECT
)) return;
2266 item
->fState
&= ~MF_MOUSESELECT
;
2267 hsubmenu
= item
->hSubMenu
;
2270 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2271 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2272 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2273 DestroyWindow( submenu
->hWnd
);
2276 if (!(wFlags
& TPM_NONOTIFY
))
2277 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2278 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2283 /***********************************************************************
2286 * Display the sub-menu of the selected item of this menu.
2287 * Return the handle of the submenu, or hmenu if no submenu to display.
2289 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2290 BOOL selectFirst
, UINT wFlags
)
2297 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2299 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2301 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2303 item
= &menu
->items
[menu
->FocusedItem
];
2304 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2307 /* message must be sent before using item,
2308 because nearly everything may be changed by the application ! */
2310 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2311 if (!(wFlags
& TPM_NONOTIFY
))
2312 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2313 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2315 item
= &menu
->items
[menu
->FocusedItem
];
2318 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2319 if (!(item
->fState
& MF_HILITE
))
2321 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2322 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2324 SelectObject( hdc
, get_menu_font(FALSE
));
2326 item
->fState
|= MF_HILITE
;
2327 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2328 ReleaseDC( menu
->hWnd
, hdc
);
2330 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2333 item
->fState
|= MF_MOUSESELECT
;
2335 if (IS_SYSTEM_MENU(menu
))
2337 MENU_InitSysMenuPopup(item
->hSubMenu
,
2338 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2339 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2341 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2342 rect
.top
= rect
.bottom
;
2343 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2344 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2348 GetWindowRect( menu
->hWnd
, &rect
);
2349 if (menu
->wFlags
& MF_POPUP
)
2351 RECT rc
= item
->rect
;
2353 MENU_AdjustMenuItemRect(menu
, &rc
);
2355 /* The first item in the popup menu has to be at the
2356 same y position as the focused menu item */
2357 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2358 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2359 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2360 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2361 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2365 rect
.left
+= item
->rect
.left
;
2366 rect
.top
+= item
->rect
.bottom
;
2367 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2368 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2372 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2373 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2375 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2376 return item
->hSubMenu
;
2381 /**********************************************************************
2384 HWND
MENU_IsMenuActive(void)
2389 /**********************************************************************
2392 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2394 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2396 void MENU_EndMenu( HWND hwnd
)
2399 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2400 if (menu
&& hwnd
== menu
->hwndOwner
) EndMenu();
2403 /***********************************************************************
2406 * Walks menu chain trying to find a menu pt maps to.
2408 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2410 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2411 UINT item
= menu
->FocusedItem
;
2414 /* try subpopup first (if any) */
2415 ret
= (item
!= NO_SELECTED_ITEM
&&
2416 (menu
->items
[item
].fType
& MF_POPUP
) &&
2417 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2418 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2420 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2422 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2423 if( menu
->wFlags
& MF_POPUP
)
2425 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2427 else if (ht
== HTSYSMENU
)
2428 ret
= get_win_sys_menu( menu
->hWnd
);
2429 else if (ht
== HTMENU
)
2430 ret
= GetMenu( menu
->hWnd
);
2435 /***********************************************************************
2436 * MENU_ExecFocusedItem
2438 * Execute a menu item (for instance when user pressed Enter).
2439 * Return the wID of the executed item. Otherwise, -1 indicating
2440 * that no menu item was executed, -2 if a popup is shown;
2441 * Have to receive the flags for the TrackPopupMenu options to avoid
2442 * sending unwanted message.
2445 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2448 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2450 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2452 if (!menu
|| !menu
->nItems
||
2453 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2455 item
= &menu
->items
[menu
->FocusedItem
];
2457 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2459 if (!(item
->fType
& MF_POPUP
))
2461 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2463 /* If TPM_RETURNCMD is set you return the id, but
2464 do not send a message to the owner */
2465 if(!(wFlags
& TPM_RETURNCMD
))
2467 if( menu
->wFlags
& MF_SYSMENU
)
2468 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2469 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2472 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2473 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2475 if (dwStyle
& MNS_NOTIFYBYPOS
)
2476 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2479 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2487 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2494 /***********************************************************************
2495 * MENU_SwitchTracking
2497 * Helper function for menu navigation routines.
2499 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2501 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2502 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2504 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2506 if( pmt
->hTopMenu
!= hPtMenu
&&
2507 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2509 /* both are top level menus (system and menu-bar) */
2510 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2511 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2512 pmt
->hTopMenu
= hPtMenu
;
2514 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2515 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2519 /***********************************************************************
2522 * Return TRUE if we can go on with menu tracking.
2524 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2526 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2531 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2534 if( IS_SYSTEM_MENU(ptmenu
) )
2535 item
= ptmenu
->items
;
2537 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2541 if( ptmenu
->FocusedItem
!= id
)
2542 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2544 /* If the popup menu is not already "popped" */
2545 if(!(item
->fState
& MF_MOUSESELECT
))
2547 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2552 /* Else the click was on the menu bar, finish the tracking */
2557 /***********************************************************************
2560 * Return the value of MENU_ExecFocusedItem if
2561 * the selected item was not a popup. Else open the popup.
2562 * A -1 return value indicates that we go on with menu tracking.
2565 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2567 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2572 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2575 if( IS_SYSTEM_MENU(ptmenu
) )
2576 item
= ptmenu
->items
;
2578 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2580 if( item
&& (ptmenu
->FocusedItem
== id
))
2582 debug_print_menuitem ("FocusedItem: ", item
, "");
2584 if( !(item
->fType
& MF_POPUP
) )
2586 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2587 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2588 return executedMenuId
;
2591 /* If we are dealing with the top-level menu */
2592 /* and this is a click on an already "popped" item: */
2593 /* Stop the menu tracking and close the opened submenus */
2594 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2597 ptmenu
->bTimeToHide
= TRUE
;
2603 /***********************************************************************
2606 * Return TRUE if we can go on with menu tracking.
2608 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2610 UINT id
= NO_SELECTED_ITEM
;
2611 POPUPMENU
*ptmenu
= NULL
;
2615 ptmenu
= MENU_GetMenu( hPtMenu
);
2616 if( IS_SYSTEM_MENU(ptmenu
) )
2619 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2622 if( id
== NO_SELECTED_ITEM
)
2624 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2625 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2628 else if( ptmenu
->FocusedItem
!= id
)
2630 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2631 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2637 /***********************************************************************
2640 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2642 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2644 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2647 /* When skipping left, we need to do something special after the
2649 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2653 /* When skipping right, for the non-system menu, we need to
2654 handle the last non-special menu item (ie skip any window
2655 icons such as MDI maximize, restore or close) */
2656 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2658 UINT i
= menu
->FocusedItem
+ 1;
2659 while (i
< menu
->nItems
) {
2660 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2661 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2665 if (i
== menu
->nItems
) {
2669 /* When skipping right, we need to cater for the system menu */
2670 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2672 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2679 MDINEXTMENU next_menu
;
2684 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2685 next_menu
.hmenuNext
= 0;
2686 next_menu
.hwndNext
= 0;
2687 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2689 TRACE("%p [%p] -> %p [%p]\n",
2690 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2692 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2694 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2695 hNewWnd
= pmt
->hOwnerWnd
;
2696 if( IS_SYSTEM_MENU(menu
) )
2698 /* switch to the menu bar */
2700 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2704 menu
= MENU_GetMenu( hNewMenu
);
2705 id
= menu
->nItems
- 1;
2707 /* Skip backwards over any system predefined icons,
2708 eg. MDI close, restore etc icons */
2710 (menu
->items
[id
].wID
>= SC_SIZE
&&
2711 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2714 else if (style
& WS_SYSMENU
)
2716 /* switch to the system menu */
2717 hNewMenu
= get_win_sys_menu( hNewWnd
);
2721 else /* application returned a new menu to switch to */
2723 hNewMenu
= next_menu
.hmenuNext
;
2724 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2726 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2728 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2730 if (style
& WS_SYSMENU
&&
2731 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2733 /* get the real system menu */
2734 hNewMenu
= get_win_sys_menu(hNewWnd
);
2736 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2738 /* FIXME: Not sure what to do here;
2739 * perhaps try to track hNewMenu as a popup? */
2741 TRACE(" -- got confused.\n");
2748 if( hNewMenu
!= pmt
->hTopMenu
)
2750 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2752 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2753 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2756 if( hNewWnd
!= pmt
->hOwnerWnd
)
2758 pmt
->hOwnerWnd
= hNewWnd
;
2759 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2762 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2763 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2770 /***********************************************************************
2773 * The idea is not to show the popup if the next input message is
2774 * going to hide it anyway.
2776 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2780 msg
.hwnd
= pmt
->hOwnerWnd
;
2782 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2783 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2788 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2789 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2791 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2792 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2793 if( msg
.message
== WM_KEYDOWN
&&
2794 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2796 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2803 /* failures go through this */
2804 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2808 /***********************************************************************
2811 * Handle a VK_ESCAPE key event in a menu.
2813 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2815 BOOL bEndMenu
= TRUE
;
2817 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2819 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2821 if (menu
->wFlags
& MF_POPUP
)
2823 HMENU hmenutmp
, hmenuprev
;
2825 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2827 /* close topmost popup */
2828 while (hmenutmp
!= pmt
->hCurrentMenu
)
2830 hmenuprev
= hmenutmp
;
2831 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2834 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2835 pmt
->hCurrentMenu
= hmenuprev
;
2843 /***********************************************************************
2846 * Handle a VK_LEFT key event in a menu.
2848 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2851 HMENU hmenutmp
, hmenuprev
;
2854 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2855 menu
= MENU_GetMenu( hmenutmp
);
2857 /* Try to move 1 column left (if possible) */
2858 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2859 NO_SELECTED_ITEM
) {
2861 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2866 /* close topmost popup */
2867 while (hmenutmp
!= pmt
->hCurrentMenu
)
2869 hmenuprev
= hmenutmp
;
2870 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2873 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2874 pmt
->hCurrentMenu
= hmenuprev
;
2876 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2878 /* move menu bar selection if no more popups are left */
2880 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2881 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2883 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2885 /* A sublevel menu was displayed - display the next one
2886 * unless there is another displacement coming up */
2888 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2889 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2890 pmt
->hTopMenu
, TRUE
, wFlags
);
2896 /***********************************************************************
2899 * Handle a VK_RIGHT key event in a menu.
2901 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2904 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2907 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2909 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2910 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2912 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2914 /* If already displaying a popup, try to display sub-popup */
2916 hmenutmp
= pmt
->hCurrentMenu
;
2917 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2919 /* if subpopup was displayed then we are done */
2920 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2923 /* Check to see if there's another column */
2924 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2925 NO_SELECTED_ITEM
) {
2926 TRACE("Going to %d.\n", nextcol
);
2927 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2932 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2934 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2936 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2937 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2938 } else hmenutmp
= 0;
2940 /* try to move to the next item */
2941 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2942 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2944 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2945 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2946 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2947 pmt
->hTopMenu
, TRUE
, wFlags
);
2951 static void CALLBACK
release_capture( BOOL __normal
)
2953 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2956 /***********************************************************************
2959 * Menu tracking code.
2961 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2962 HWND hwnd
, const RECT
*lprect
)
2967 INT executedMenuId
= -1;
2969 BOOL enterIdleSent
= FALSE
;
2973 mt
.hCurrentMenu
= hmenu
;
2974 mt
.hTopMenu
= hmenu
;
2975 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2979 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2980 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2983 if (!(menu
= MENU_GetMenu( hmenu
)))
2985 WARN("Invalid menu handle %p\n", hmenu
);
2986 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2990 if (wFlags
& TPM_BUTTONDOWN
)
2992 /* Get the result in order to start the tracking or not */
2993 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2994 fEndMenu
= !fRemove
;
2997 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
2999 /* owner may not be visible when tracking a popup, so use the menu itself */
3000 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3001 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3003 __TRY
while (!fEndMenu
)
3005 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3006 if (!menu
) /* sometimes happens if I do a window manager close */
3009 /* we have to keep the message in the queue until it's
3010 * clear that menu loop is not over yet. */
3014 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3016 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3017 /* remove the message from the queue */
3018 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3024 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3025 enterIdleSent
= TRUE
;
3026 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3032 /* check if EndMenu() tried to cancel us, by posting this message */
3033 if(msg
.message
== WM_CANCELMODE
)
3035 /* we are now out of the loop */
3038 /* remove the message from the queue */
3039 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3041 /* break out of internal loop, ala ESCAPE */
3045 TranslateMessage( &msg
);
3048 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3049 enterIdleSent
=FALSE
;
3052 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3055 * Use the mouse coordinates in lParam instead of those in the MSG
3056 * struct to properly handle synthetic messages. They are already
3057 * in screen coordinates.
3059 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3060 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3062 /* Find a menu for this mouse event */
3063 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3067 /* no WM_NC... messages in captured state */
3069 case WM_RBUTTONDBLCLK
:
3070 case WM_RBUTTONDOWN
:
3071 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3073 case WM_LBUTTONDBLCLK
:
3074 case WM_LBUTTONDOWN
:
3075 /* If the message belongs to the menu, removes it from the queue */
3076 /* Else, end menu tracking */
3077 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3078 fEndMenu
= !fRemove
;
3082 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3085 /* Check if a menu was selected by the mouse */
3088 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3089 TRACE("executedMenuId %d\n", executedMenuId
);
3091 /* End the loop if executedMenuId is an item ID */
3092 /* or if the job was done (executedMenuId = 0). */
3093 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3095 /* No menu was selected by the mouse */
3096 /* if the function was called by TrackPopupMenu, continue
3097 with the menu tracking. If not, stop it */
3099 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3104 /* the selected menu item must be changed every time */
3105 /* the mouse moves. */
3108 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3110 } /* switch(msg.message) - mouse */
3112 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3114 fRemove
= TRUE
; /* Keyboard messages are always removed */
3128 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3129 NO_SELECTED_ITEM
, FALSE
, 0 );
3130 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3131 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3135 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3137 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3138 if (!(menu
->wFlags
& MF_POPUP
))
3139 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3140 else /* otherwise try to move selection */
3141 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3142 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3146 MENU_KeyLeft( &mt
, wFlags
);
3150 MENU_KeyRight( &mt
, wFlags
);
3154 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3160 hi
.cbSize
= sizeof(HELPINFO
);
3161 hi
.iContextType
= HELPINFO_MENUITEM
;
3162 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3165 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3166 hi
.hItemHandle
= hmenu
;
3167 hi
.dwContextId
= menu
->dwContextHelpID
;
3168 hi
.MousePos
= msg
.pt
;
3169 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3176 break; /* WM_KEYDOWN */
3183 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3185 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3186 fEndMenu
= (executedMenuId
!= -2);
3191 /* Hack to avoid control chars. */
3192 /* We will find a better way real soon... */
3193 if (msg
.wParam
< 32) break;
3195 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3196 LOWORD(msg
.wParam
), FALSE
);
3197 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3198 else if (pos
== (UINT
)-1) MessageBeep(0);
3201 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3203 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3204 fEndMenu
= (executedMenuId
!= -2);
3208 } /* switch(msg.message) - kbd */
3212 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3213 DispatchMessageW( &msg
);
3217 if (!fEndMenu
) fRemove
= TRUE
;
3219 /* finally remove message from the queue */
3221 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3222 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3223 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3225 __FINALLY( release_capture
)
3227 /* If dropdown is still painted and the close box is clicked on
3228 then the menu will be destroyed as part of the DispatchMessage above.
3229 This will then invalidate the menu handle in mt.hTopMenu. We should
3230 check for this first. */
3231 if( IsMenu( mt
.hTopMenu
) )
3233 menu
= MENU_GetMenu( mt
.hTopMenu
);
3235 if( IsWindow( mt
.hOwnerWnd
) )
3237 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3239 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3241 DestroyWindow( menu
->hWnd
);
3244 if (!(wFlags
& TPM_NONOTIFY
))
3245 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3246 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3248 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3249 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3252 /* Reset the variable for hiding menu */
3253 if( menu
) menu
->bTimeToHide
= FALSE
;
3256 /* The return value is only used by TrackPopupMenu */
3257 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3258 if (executedMenuId
== -1) executedMenuId
= 0;
3259 return executedMenuId
;
3262 /***********************************************************************
3265 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3269 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3273 /* This makes the menus of applications built with Delphi work.
3274 * It also enables menus to be displayed in more than one window,
3275 * but there are some bugs left that need to be fixed in this case.
3277 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3279 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3280 if (!(wFlags
& TPM_NONOTIFY
))
3281 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3283 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3285 if (!(wFlags
& TPM_NONOTIFY
))
3287 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3288 /* If an app changed/recreated menu bar entries in WM_INITMENU
3289 * menu sizes will be recalculated once the menu created/shown.
3296 /***********************************************************************
3299 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3301 TRACE("hwnd=%p\n", hWnd
);
3303 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3306 top_popup_hmenu
= NULL
;
3310 /***********************************************************************
3311 * MENU_TrackMouseMenuBar
3313 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3315 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3317 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3318 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3320 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3324 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3325 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3326 MENU_ExitTracking(hWnd
, FALSE
);
3331 /***********************************************************************
3332 * MENU_TrackKbdMenuBar
3334 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3336 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3338 UINT uItem
= NO_SELECTED_ITEM
;
3340 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3342 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3344 /* find window that has a menu */
3346 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3347 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3349 /* check if we have to track a system menu */
3351 hTrackMenu
= GetMenu( hwnd
);
3352 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3354 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3355 hTrackMenu
= get_win_sys_menu( hwnd
);
3357 wParam
|= HTSYSMENU
; /* prevent item lookup */
3360 if (!IsMenu( hTrackMenu
)) return;
3362 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3364 if( wChar
&& wChar
!= ' ' )
3366 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3367 if ( uItem
>= (UINT
)(-2) )
3369 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3370 /* schedule end of menu tracking */
3371 wFlags
|= TF_ENDMENU
;
3376 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3378 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3380 if( uItem
== NO_SELECTED_ITEM
)
3381 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3383 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3387 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3388 MENU_ExitTracking( hwnd
, FALSE
);
3391 /**********************************************************************
3392 * TrackPopupMenuEx (USER32.@)
3394 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3395 HWND hWnd
, LPTPMPARAMS lpTpm
)
3400 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3401 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3402 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3404 /* Parameter check */
3405 /* FIXME: this check is performed several times, here and in the called
3406 functions. That could be optimized */
3407 if (!(menu
= MENU_GetMenu( hMenu
)))
3409 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3413 if (IsWindow(menu
->hWnd
))
3415 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3419 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3421 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3422 if (!(wFlags
& TPM_NONOTIFY
))
3423 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3425 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3426 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3427 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3428 MENU_ExitTracking(hWnd
, TRUE
);
3433 /**********************************************************************
3434 * TrackPopupMenu (USER32.@)
3436 * Like the win32 API, the function return the command ID only if the
3437 * flag TPM_RETURNCMD is on.
3440 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3441 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3443 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3446 /***********************************************************************
3449 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3451 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3453 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3459 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3460 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3464 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3465 return MA_NOACTIVATE
;
3470 BeginPaint( hwnd
, &ps
);
3471 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3472 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3473 EndPaint( hwnd
, &ps
);
3477 case WM_PRINTCLIENT
:
3479 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3480 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3488 /* zero out global pointer in case resident popup window was destroyed. */
3489 if (hwnd
== top_popup
) {
3491 top_popup_hmenu
= NULL
;
3499 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3502 SetWindowLongPtrW( hwnd
, 0, 0 );
3505 case MM_SETMENUHANDLE
:
3506 SetWindowLongPtrW( hwnd
, 0, wParam
);
3509 case MM_GETMENUHANDLE
:
3511 return GetWindowLongPtrW( hwnd
, 0 );
3514 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3520 /***********************************************************************
3521 * MENU_GetMenuBarHeight
3523 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3525 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3526 INT orgX
, INT orgY
)
3532 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3534 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3536 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3537 SelectObject( hdc
, get_menu_font(FALSE
));
3538 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3539 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3540 ReleaseDC( hwnd
, hdc
);
3541 return lppop
->Height
;
3545 /*******************************************************************
3546 * ChangeMenuA (USER32.@)
3548 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3549 UINT id
, UINT flags
)
3551 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3552 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3554 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3555 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3557 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3558 flags
& MF_BYPOSITION
? pos
: id
,
3559 flags
& ~MF_REMOVE
);
3560 /* Default: MF_INSERT */
3561 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3565 /*******************************************************************
3566 * ChangeMenuW (USER32.@)
3568 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3569 UINT id
, UINT flags
)
3571 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3572 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3574 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3575 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3577 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3578 flags
& MF_BYPOSITION
? pos
: id
,
3579 flags
& ~MF_REMOVE
);
3580 /* Default: MF_INSERT */
3581 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3585 /*******************************************************************
3586 * CheckMenuItem (USER32.@)
3588 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3593 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3594 ret
= item
->fState
& MF_CHECKED
;
3595 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3596 else item
->fState
&= ~MF_CHECKED
;
3601 /**********************************************************************
3602 * EnableMenuItem (USER32.@)
3604 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3610 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3612 /* Get the Popupmenu to access the owner menu */
3613 if (!(menu
= MENU_GetMenu(hMenu
)))
3616 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3619 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3620 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3622 /* If the close item in the system menu change update the close button */
3623 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3625 if (menu
->hSysMenuOwner
!= 0)
3628 POPUPMENU
* parentMenu
;
3630 /* Get the parent menu to access*/
3631 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3634 /* Refresh the frame to reflect the change */
3635 GetWindowRect(parentMenu
->hWnd
, &rc
);
3636 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3638 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3646 /*******************************************************************
3647 * GetMenuStringA (USER32.@)
3649 INT WINAPI
GetMenuStringA(
3650 HMENU hMenu
, /* [in] menuhandle */
3651 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3652 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3653 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3654 UINT wFlags
/* [in] MF_ flags */
3658 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3659 if (str
&& nMaxSiz
) str
[0] = '\0';
3660 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3661 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3664 if (!item
->text
) return 0;
3665 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3666 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3668 TRACE("returning %s\n", debugstr_a(str
));
3673 /*******************************************************************
3674 * GetMenuStringW (USER32.@)
3676 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3677 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3681 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3682 if (str
&& nMaxSiz
) str
[0] = '\0';
3683 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3684 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3687 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3688 if( !(item
->text
)) {
3692 lstrcpynW( str
, item
->text
, nMaxSiz
);
3693 TRACE("returning %s\n", debugstr_w(str
));
3694 return strlenW(str
);
3698 /**********************************************************************
3699 * HiliteMenuItem (USER32.@)
3701 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3705 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3706 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3707 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3708 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3709 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3710 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3715 /**********************************************************************
3716 * GetMenuState (USER32.@)
3718 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3721 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3722 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3723 debug_print_menuitem (" item: ", item
, "");
3724 if (item
->fType
& MF_POPUP
)
3726 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3727 if (!menu
) return -1;
3728 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3732 /* We used to (from way back then) mask the result to 0xff. */
3733 /* I don't know why and it seems wrong as the documented */
3734 /* return flag MF_SEPARATOR is outside that mask. */
3735 return (item
->fType
| item
->fState
);
3740 /**********************************************************************
3741 * GetMenuItemCount (USER32.@)
3743 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3745 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3746 if (!menu
) return -1;
3747 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3748 return menu
->nItems
;
3752 /**********************************************************************
3753 * GetMenuItemID (USER32.@)
3755 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3759 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3760 if (lpmi
->fType
& MF_POPUP
) return -1;
3766 /**********************************************************************
3769 * Uses flags, id and text ptr, passed by InsertMenu() and
3770 * ModifyMenu() to setup a MenuItemInfo structure.
3772 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3773 LPMENUITEMINFOW pmii
)
3775 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3776 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3777 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3778 /* setting bitmap clears text and vice versa */
3779 if( IS_STRING_ITEM(flags
)) {
3780 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3782 flags
|= MF_SEPARATOR
;
3783 /* Item beginning with a backspace is a help item */
3784 /* FIXME: wrong place, this is only true in win16 */
3785 else if( *str
== '\b') {
3789 pmii
->dwTypeData
= (LPWSTR
)str
;
3790 } else if( flags
& MFT_BITMAP
){
3791 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3792 pmii
->hbmpItem
= ULongToHandle(LOWORD(str
));
3794 if( flags
& MF_OWNERDRAW
){
3795 pmii
->fMask
|= MIIM_DATA
;
3796 pmii
->dwItemData
= (ULONG_PTR
) str
;
3798 if( flags
& MF_POPUP
) {
3799 pmii
->fMask
|= MIIM_SUBMENU
;
3800 pmii
->hSubMenu
= (HMENU
)id
;
3802 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3803 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3804 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3805 pmii
->wID
= (UINT
)id
;
3809 /*******************************************************************
3810 * InsertMenuW (USER32.@)
3812 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3813 UINT_PTR id
, LPCWSTR str
)
3818 if (IS_STRING_ITEM(flags
) && str
)
3819 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3820 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3821 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3822 hMenu
, pos
, flags
, id
, str
);
3824 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3825 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3826 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3828 RemoveMenu( hMenu
, pos
, flags
);
3832 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3837 /*******************************************************************
3838 * InsertMenuA (USER32.@)
3840 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3841 UINT_PTR id
, LPCSTR str
)
3845 if (IS_STRING_ITEM(flags
) && str
)
3847 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3848 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3851 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3852 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3853 HeapFree( GetProcessHeap(), 0, newstr
);
3857 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3861 /*******************************************************************
3862 * AppendMenuA (USER32.@)
3864 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3865 UINT_PTR id
, LPCSTR data
)
3867 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3871 /*******************************************************************
3872 * AppendMenuW (USER32.@)
3874 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3875 UINT_PTR id
, LPCWSTR data
)
3877 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3881 /**********************************************************************
3882 * RemoveMenu (USER32.@)
3884 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3889 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3890 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3891 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3895 MENU_FreeItemData( item
);
3897 if (--menu
->nItems
== 0)
3899 HeapFree( GetProcessHeap(), 0, menu
->items
);
3904 while(nPos
< menu
->nItems
)
3910 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3911 menu
->nItems
* sizeof(MENUITEM
) );
3917 /**********************************************************************
3918 * DeleteMenu (USER32.@)
3920 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3922 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3923 if (!item
) return FALSE
;
3924 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3925 /* nPos is now the position of the item */
3926 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3931 /*******************************************************************
3932 * ModifyMenuW (USER32.@)
3934 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3935 UINT_PTR id
, LPCWSTR str
)
3940 if (IS_STRING_ITEM(flags
))
3941 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3943 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3945 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3946 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3947 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3948 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
3952 /*******************************************************************
3953 * ModifyMenuA (USER32.@)
3955 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3956 UINT_PTR id
, LPCSTR str
)
3960 if (IS_STRING_ITEM(flags
) && str
)
3962 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3963 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3966 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3967 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3968 HeapFree( GetProcessHeap(), 0, newstr
);
3972 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3976 /**********************************************************************
3977 * CreatePopupMenu (USER32.@)
3979 HMENU WINAPI
CreatePopupMenu(void)
3984 if (!(hmenu
= CreateMenu())) return 0;
3985 menu
= MENU_GetMenu( hmenu
);
3986 menu
->wFlags
|= MF_POPUP
;
3987 menu
->bTimeToHide
= FALSE
;
3992 /**********************************************************************
3993 * GetMenuCheckMarkDimensions (USER.417)
3994 * GetMenuCheckMarkDimensions (USER32.@)
3996 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3998 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4002 /**********************************************************************
4003 * SetMenuItemBitmaps (USER32.@)
4005 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4006 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4010 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4012 if (!hNewCheck
&& !hNewUnCheck
)
4014 item
->fState
&= ~MF_USECHECKBITMAPS
;
4016 else /* Install new bitmaps */
4018 item
->hCheckBit
= hNewCheck
;
4019 item
->hUnCheckBit
= hNewUnCheck
;
4020 item
->fState
|= MF_USECHECKBITMAPS
;
4026 /**********************************************************************
4027 * CreateMenu (USER32.@)
4029 HMENU WINAPI
CreateMenu(void)
4034 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4035 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4036 menu
->bTimeToHide
= FALSE
;
4038 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4040 TRACE("return %p\n", hMenu
);
4046 /**********************************************************************
4047 * DestroyMenu (USER32.@)
4049 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4053 TRACE("(%p)\n", hMenu
);
4055 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4056 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4058 /* DestroyMenu should not destroy system menu popup owner */
4059 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4061 DestroyWindow( lppop
->hWnd
);
4065 if (lppop
->items
) /* recursively destroy submenus */
4068 MENUITEM
*item
= lppop
->items
;
4069 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4071 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4072 MENU_FreeItemData( item
);
4074 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4076 HeapFree( GetProcessHeap(), 0, lppop
);
4081 /**********************************************************************
4082 * GetSystemMenu (USER32.@)
4084 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4086 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4089 if (wndPtr
== WND_DESKTOP
) return 0;
4090 if (wndPtr
== WND_OTHER_PROCESS
)
4092 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4096 if (wndPtr
->hSysMenu
&& bRevert
)
4098 DestroyMenu(wndPtr
->hSysMenu
);
4099 wndPtr
->hSysMenu
= 0;
4102 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4103 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4105 if( wndPtr
->hSysMenu
)
4108 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4110 /* Store the dummy sysmenu handle to facilitate the refresh */
4111 /* of the close button if the SC_CLOSE item change */
4112 menu
= MENU_GetMenu(retvalue
);
4114 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4116 WIN_ReleasePtr( wndPtr
);
4118 return bRevert
? 0 : retvalue
;
4122 /*******************************************************************
4123 * SetSystemMenu (USER32.@)
4125 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4127 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4129 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4131 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4132 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4133 WIN_ReleasePtr( wndPtr
);
4140 /**********************************************************************
4141 * GetMenu (USER32.@)
4143 HMENU WINAPI
GetMenu( HWND hWnd
)
4145 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4146 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4150 /**********************************************************************
4151 * GetMenuBarInfo (USER32.@)
4153 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4155 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4159 /**********************************************************************
4162 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4163 * SetWindowPos call that would result if SetMenu were called directly.
4165 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4167 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4169 if (hMenu
&& !IsMenu(hMenu
))
4171 WARN("hMenu %p is not a menu handle\n", hMenu
);
4174 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4177 hWnd
= WIN_GetFullHandle( hWnd
);
4178 if (GetCapture() == hWnd
)
4179 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4185 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4187 lpmenu
->hWnd
= hWnd
;
4188 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4190 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4195 /**********************************************************************
4196 * SetMenu (USER32.@)
4198 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4200 if(!MENU_SetMenu(hWnd
, hMenu
))
4203 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4204 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4209 /**********************************************************************
4210 * GetSubMenu (USER32.@)
4212 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4216 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4217 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4218 return lpmi
->hSubMenu
;
4222 /**********************************************************************
4223 * DrawMenuBar (USER32.@)
4225 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4228 HMENU hMenu
= GetMenu(hWnd
);
4230 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4232 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4234 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4235 lppop
->hwndOwner
= hWnd
;
4236 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4237 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4241 /***********************************************************************
4242 * DrawMenuBarTemp (USER32.@)
4246 * called by W98SE desk.cpl Control Panel Applet
4248 * Not 100% sure about the param names, but close.
4250 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4255 BOOL flat_menu
= FALSE
;
4257 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4260 hMenu
= GetMenu(hwnd
);
4263 hFont
= get_menu_font(FALSE
);
4265 lppop
= MENU_GetMenu( hMenu
);
4266 if (lppop
== NULL
|| lprect
== NULL
)
4268 retvalue
= GetSystemMetrics(SM_CYMENU
);
4272 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4274 hfontOld
= SelectObject( hDC
, hFont
);
4276 if (lppop
->Height
== 0)
4277 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4279 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4281 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4283 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4284 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4285 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4287 if (lppop
->nItems
== 0)
4289 retvalue
= GetSystemMetrics(SM_CYMENU
);
4293 for (i
= 0; i
< lppop
->nItems
; i
++)
4295 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4296 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4298 retvalue
= lppop
->Height
;
4301 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4305 /***********************************************************************
4306 * EndMenu (USER.187)
4307 * EndMenu (USER32.@)
4309 BOOL WINAPI
EndMenu(void)
4311 /* if we are in the menu code, and it is active */
4312 if (!fEndMenu
&& top_popup
)
4314 /* terminate the menu handling code */
4317 /* needs to be posted to wakeup the internal menu handler */
4318 /* which will now terminate the menu, in the event that */
4319 /* the main window was minimized, or lost focus, so we */
4320 /* don't end up with an orphaned menu */
4321 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4327 /*****************************************************************
4328 * LoadMenuA (USER32.@)
4330 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4332 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4333 if (!hrsrc
) return 0;
4334 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4338 /*****************************************************************
4339 * LoadMenuW (USER32.@)
4341 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4343 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4344 if (!hrsrc
) return 0;
4345 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4349 /**********************************************************************
4350 * LoadMenuIndirectW (USER32.@)
4352 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4355 WORD version
, offset
;
4356 LPCSTR p
= template;
4358 version
= GET_WORD(p
);
4360 TRACE("%p, ver %d\n", template, version
);
4363 case 0: /* standard format is version of 0 */
4364 offset
= GET_WORD(p
);
4365 p
+= sizeof(WORD
) + offset
;
4366 if (!(hMenu
= CreateMenu())) return 0;
4367 if (!MENU_ParseResource( p
, hMenu
))
4369 DestroyMenu( hMenu
);
4373 case 1: /* extended format is version of 1 */
4374 offset
= GET_WORD(p
);
4375 p
+= sizeof(WORD
) + offset
;
4376 if (!(hMenu
= CreateMenu())) return 0;
4377 if (!MENUEX_ParseResource( p
, hMenu
))
4379 DestroyMenu( hMenu
);
4384 ERR("version %d not supported.\n", version
);
4390 /**********************************************************************
4391 * LoadMenuIndirectA (USER32.@)
4393 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4395 return LoadMenuIndirectW( template );
4399 /**********************************************************************
4402 BOOL WINAPI
IsMenu(HMENU hmenu
)
4404 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4408 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4414 /**********************************************************************
4415 * GetMenuItemInfo_common
4418 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4419 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4421 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4423 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4426 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4430 if( lpmii
->fMask
& MIIM_TYPE
) {
4431 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4432 WARN("invalid combination of fMask bits used\n");
4433 /* this does not happen on Win9x/ME */
4434 SetLastError( ERROR_INVALID_PARAMETER
);
4437 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4438 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4439 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4440 if( lpmii
->fType
& MFT_BITMAP
) {
4441 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4443 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4444 /* this does not happen on Win9x/ME */
4445 lpmii
->dwTypeData
= 0;
4450 /* copy the text string */
4451 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4453 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4456 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4458 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4464 len
= strlenW(menu
->text
);
4465 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4466 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4470 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4471 0, NULL
, NULL
) - 1;
4472 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4473 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4474 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4475 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4477 /* if we've copied a substring we return its length */
4478 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4479 if (lpmii
->cch
<= len
+ 1)
4484 /* return length of string */
4485 /* not on Win9x/ME if fType & MFT_BITMAP */
4491 if (lpmii
->fMask
& MIIM_FTYPE
)
4492 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4494 if (lpmii
->fMask
& MIIM_BITMAP
)
4495 lpmii
->hbmpItem
= menu
->hbmpItem
;
4497 if (lpmii
->fMask
& MIIM_STATE
)
4498 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4500 if (lpmii
->fMask
& MIIM_ID
)
4501 lpmii
->wID
= menu
->wID
;
4503 if (lpmii
->fMask
& MIIM_SUBMENU
)
4504 lpmii
->hSubMenu
= menu
->hSubMenu
;
4506 /* hSubMenu is always cleared
4507 * (not on Win9x/ME ) */
4508 lpmii
->hSubMenu
= 0;
4511 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4512 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4513 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4515 if (lpmii
->fMask
& MIIM_DATA
)
4516 lpmii
->dwItemData
= menu
->dwItemData
;
4521 /**********************************************************************
4522 * GetMenuItemInfoA (USER32.@)
4524 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4525 LPMENUITEMINFOA lpmii
)
4529 if( lpmii
->cbSize
!= sizeof( mii
) &&
4530 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4531 SetLastError( ERROR_INVALID_PARAMETER
);
4534 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4535 mii
.cbSize
= sizeof( mii
);
4536 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4537 (LPMENUITEMINFOW
)&mii
, FALSE
);
4538 mii
.cbSize
= lpmii
->cbSize
;
4539 memcpy( lpmii
, &mii
, mii
.cbSize
);
4543 /**********************************************************************
4544 * GetMenuItemInfoW (USER32.@)
4546 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4547 LPMENUITEMINFOW lpmii
)
4551 if( lpmii
->cbSize
!= sizeof( mii
) &&
4552 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4553 SetLastError( ERROR_INVALID_PARAMETER
);
4556 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4557 mii
.cbSize
= sizeof( mii
);
4558 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4559 mii
.cbSize
= lpmii
->cbSize
;
4560 memcpy( lpmii
, &mii
, mii
.cbSize
);
4565 /* set a menu item text from a ASCII or Unicode string */
4566 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4572 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4573 strcpyW( menu
->text
, text
);
4577 LPCSTR str
= (LPCSTR
)text
;
4578 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4579 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4580 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4585 /**********************************************************************
4588 * detect if there are loops in the menu tree (or the depth is too large)
4590 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4597 if( depth
> MAXMENUDEPTH
) return depth
;
4598 item
= pmenu
->items
;
4600 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4601 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4603 int bdepth
= MENU_depth( psubmenu
, depth
);
4604 if( bdepth
> subdepth
) subdepth
= bdepth
;
4606 if( subdepth
> MAXMENUDEPTH
)
4607 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4613 /**********************************************************************
4614 * SetMenuItemInfo_common
4616 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4617 * MIIM_BITMAP and MIIM_STRING flags instead.
4620 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4621 const MENUITEMINFOW
*lpmii
,
4624 if (!menu
) return FALSE
;
4626 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4628 if (lpmii
->fMask
& MIIM_FTYPE
) {
4629 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4630 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4632 if (lpmii
->fMask
& MIIM_STRING
) {
4633 /* free the string when used */
4634 HeapFree(GetProcessHeap(), 0, menu
->text
);
4635 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4638 if (lpmii
->fMask
& MIIM_STATE
)
4639 /* Other menu items having MFS_DEFAULT are not converted
4641 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4643 if (lpmii
->fMask
& MIIM_ID
)
4644 menu
->wID
= lpmii
->wID
;
4646 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4647 menu
->hSubMenu
= lpmii
->hSubMenu
;
4648 if (menu
->hSubMenu
) {
4649 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4651 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4652 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4656 subMenu
->wFlags
|= MF_POPUP
;
4657 menu
->fType
|= MF_POPUP
;
4659 SetLastError( ERROR_INVALID_PARAMETER
);
4664 menu
->fType
&= ~MF_POPUP
;
4667 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4669 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4670 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4672 if (lpmii
->fMask
& MIIM_DATA
)
4673 menu
->dwItemData
= lpmii
->dwItemData
;
4675 if (lpmii
->fMask
& MIIM_BITMAP
)
4676 menu
->hbmpItem
= lpmii
->hbmpItem
;
4678 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4679 menu
->fType
|= MFT_SEPARATOR
;
4681 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4685 /**********************************************************************
4686 * MENU_NormalizeMenuItemInfoStruct
4688 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4689 * check, copy and extend the MENUITEMINFO struct from the version that the application
4690 * supplied to the version used by wine source. */
4691 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4692 MENUITEMINFOW
*pmii_out
)
4694 /* do we recognize the size? */
4695 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4696 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) {
4697 SetLastError( ERROR_INVALID_PARAMETER
);
4700 /* copy the fields that we have */
4701 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4702 /* if the hbmpItem member is missing then extend */
4703 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4704 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4705 pmii_out
->hbmpItem
= NULL
;
4707 /* test for invalid bit combinations */
4708 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4709 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4710 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4711 WARN("invalid combination of fMask bits used\n");
4712 /* this does not happen on Win9x/ME */
4713 SetLastError( ERROR_INVALID_PARAMETER
);
4716 /* convert old style (MIIM_TYPE) to the new */
4717 if( pmii_out
->fMask
& MIIM_TYPE
){
4718 pmii_out
->fMask
|= MIIM_FTYPE
;
4719 if( IS_STRING_ITEM(pmii_out
->fType
)){
4720 pmii_out
->fMask
|= MIIM_STRING
;
4721 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4722 pmii_out
->fMask
|= MIIM_BITMAP
;
4723 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4729 /**********************************************************************
4730 * SetMenuItemInfoA (USER32.@)
4732 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4733 const MENUITEMINFOA
*lpmii
)
4737 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4739 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4741 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4745 /**********************************************************************
4746 * SetMenuItemInfoW (USER32.@)
4748 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4749 const MENUITEMINFOW
*lpmii
)
4753 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4755 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4756 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4757 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4760 /**********************************************************************
4761 * SetMenuDefaultItem (USER32.@)
4764 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4770 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4772 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4774 /* reset all default-item flags */
4776 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4778 item
->fState
&= ~MFS_DEFAULT
;
4781 /* no default item */
4790 if ( uItem
>= menu
->nItems
) return FALSE
;
4791 item
[uItem
].fState
|= MFS_DEFAULT
;
4796 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4798 if (item
->wID
== uItem
)
4800 item
->fState
|= MFS_DEFAULT
;
4809 /**********************************************************************
4810 * GetMenuDefaultItem (USER32.@)
4812 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4818 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4820 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4822 /* find default item */
4826 if (! item
) return -1;
4828 while ( !( item
->fState
& MFS_DEFAULT
) )
4831 if (i
>= menu
->nItems
) return -1;
4834 /* default: don't return disabled items */
4835 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4837 /* search rekursiv when needed */
4838 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4841 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4842 if ( -1 != ret
) return ret
;
4844 /* when item not found in submenu, return the popup item */
4846 return ( bypos
) ? i
: item
->wID
;
4851 /**********************************************************************
4852 * InsertMenuItemA (USER32.@)
4854 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4855 const MENUITEMINFOA
*lpmii
)
4860 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4862 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4864 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4865 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
4869 /**********************************************************************
4870 * InsertMenuItemW (USER32.@)
4872 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4873 const MENUITEMINFOW
*lpmii
)
4878 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4880 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4882 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4883 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4886 /**********************************************************************
4887 * CheckMenuRadioItem (USER32.@)
4890 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4891 UINT first
, UINT last
, UINT check
,
4896 MENUITEM
*mi_first
= NULL
, *mi_check
;
4897 HMENU m_first
, m_check
;
4899 for (i
= first
; i
<= last
; i
++)
4906 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4907 if (!mi_first
) continue;
4908 mi_check
= mi_first
;
4914 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4915 if (!mi_check
) continue;
4918 if (m_first
!= m_check
) continue;
4919 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4923 mi_check
->fType
|= MFT_RADIOCHECK
;
4924 mi_check
->fState
|= MFS_CHECKED
;
4929 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4930 mi_check
->fState
&= ~MFS_CHECKED
;
4938 /**********************************************************************
4939 * GetMenuItemRect (USER32.@)
4941 * ATTENTION: Here, the returned values in rect are the screen
4942 * coordinates of the item just like if the menu was
4943 * always on the upper left side of the application.
4946 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4949 POPUPMENU
*itemMenu
;
4953 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4955 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4956 referenceHwnd
= hwnd
;
4960 itemMenu
= MENU_GetMenu(hMenu
);
4961 if (itemMenu
== NULL
)
4964 if(itemMenu
->hWnd
== 0)
4966 referenceHwnd
= itemMenu
->hWnd
;
4969 if ((rect
== NULL
) || (item
== NULL
))
4974 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4979 /**********************************************************************
4980 * SetMenuInfo (USER32.@)
4983 * actually use the items to draw the menu
4984 * (recalculate and/or redraw)
4986 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
4989 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
4991 if (lpmi
->fMask
& MIM_BACKGROUND
)
4992 menu
->hbrBack
= lpmi
->hbrBack
;
4994 if (lpmi
->fMask
& MIM_HELPID
)
4995 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4997 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4998 menu
->cyMax
= lpmi
->cyMax
;
5000 if (lpmi
->fMask
& MIM_MENUDATA
)
5001 menu
->dwMenuData
= lpmi
->dwMenuData
;
5003 if (lpmi
->fMask
& MIM_STYLE
)
5004 menu
->dwStyle
= lpmi
->dwStyle
;
5006 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5008 MENUITEM
*item
= menu
->items
;
5009 for( i
= menu
->nItems
; i
; i
--, item
++)
5010 if( item
->fType
& MF_POPUP
)
5011 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5016 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5018 TRACE("(%p %p)\n", hMenu
, lpmi
);
5019 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5020 if( lpmi
->fMask
& MIM_STYLE
) {
5021 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5022 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5023 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5027 SetLastError( ERROR_INVALID_PARAMETER
);
5031 /**********************************************************************
5032 * GetMenuInfo (USER32.@)
5038 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5041 TRACE("(%p %p)\n", hMenu
, lpmi
);
5043 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5046 if (lpmi
->fMask
& MIM_BACKGROUND
)
5047 lpmi
->hbrBack
= menu
->hbrBack
;
5049 if (lpmi
->fMask
& MIM_HELPID
)
5050 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5052 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5053 lpmi
->cyMax
= menu
->cyMax
;
5055 if (lpmi
->fMask
& MIM_MENUDATA
)
5056 lpmi
->dwMenuData
= menu
->dwMenuData
;
5058 if (lpmi
->fMask
& MIM_STYLE
)
5059 lpmi
->dwStyle
= menu
->dwStyle
;
5063 SetLastError( ERROR_INVALID_PARAMETER
);
5068 /**********************************************************************
5069 * SetMenuContextHelpId (USER32.@)
5071 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5075 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5077 if ((menu
= MENU_GetMenu(hMenu
)))
5079 menu
->dwContextHelpID
= dwContextHelpID
;
5086 /**********************************************************************
5087 * GetMenuContextHelpId (USER32.@)
5089 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5093 TRACE("(%p)\n", hMenu
);
5095 if ((menu
= MENU_GetMenu(hMenu
)))
5097 return menu
->dwContextHelpID
;
5102 /**********************************************************************
5103 * MenuItemFromPoint (USER32.@)
5105 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5107 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5110 /*FIXME: Do we have to handle hWnd here? */
5111 if (!menu
) return -1;
5112 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5117 /**********************************************************************
5118 * translate_accelerator
5120 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5121 BYTE fVirt
, WORD key
, WORD cmd
)
5126 if (wParam
!= key
) return FALSE
;
5128 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5129 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5130 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5132 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5134 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5136 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5142 if(fVirt
& FVIRTKEY
)
5144 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5145 wParam
, 0xff & HIWORD(lParam
));
5147 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5148 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5152 if (!(lParam
& 0x01000000)) /* no special_key */
5154 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5155 { /* ^^ ALT pressed */
5156 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5165 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5169 HMENU hMenu
, hSubMenu
, hSysMenu
;
5170 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5172 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5173 hSysMenu
= get_win_sys_menu( hWnd
);
5175 /* find menu item and ask application to initialize it */
5176 /* 1. in the system menu */
5177 hSubMenu
= hSysMenu
;
5179 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5183 if (!IsWindowEnabled(hWnd
))
5187 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5188 if(hSubMenu
!= hSysMenu
)
5190 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5191 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5192 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5194 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5197 else /* 2. in the window's menu */
5201 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5205 if (!IsWindowEnabled(hWnd
))
5209 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5210 if(hSubMenu
!= hMenu
)
5212 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5213 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5214 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5216 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5223 if (uSysStat
!= (UINT
)-1)
5225 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5232 if (uStat
!= (UINT
)-1)
5238 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5250 if( mesg
==WM_COMMAND
)
5252 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5253 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5255 else if( mesg
==WM_SYSCOMMAND
)
5257 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5258 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5262 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5263 * #0: unknown (please report!)
5264 * #1: for WM_KEYUP,WM_SYSKEYUP
5265 * #2: mouse is captured
5266 * #3: window is disabled
5267 * #4: it's a disabled system menu option
5268 * #5: it's a menu option, but window is iconic
5269 * #6: it's a menu option, but disabled
5271 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5273 ERR_(accel
)(" unknown reason - please report!\n");
5278 /**********************************************************************
5279 * TranslateAcceleratorA (USER32.@)
5280 * TranslateAccelerator (USER32.@)
5282 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5284 switch (msg
->message
)
5288 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5294 char ch
= LOWORD(msg
->wParam
);
5296 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5297 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5298 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5306 /**********************************************************************
5307 * TranslateAcceleratorW (USER32.@)
5309 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5311 ACCEL data
[32], *ptr
= data
;
5314 if (!hWnd
) return 0;
5316 if (msg
->message
!= WM_KEYDOWN
&&
5317 msg
->message
!= WM_SYSKEYDOWN
&&
5318 msg
->message
!= WM_CHAR
&&
5319 msg
->message
!= WM_SYSCHAR
)
5322 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5323 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5325 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5326 if (count
> sizeof(data
)/sizeof(data
[0]))
5328 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5330 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5331 for (i
= 0; i
< count
; i
++)
5333 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5334 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5337 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);