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;
730 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
731 else pt.x -= rect.left;
734 for (i = 0; i < menu->nItems; i++, item++)
737 MENU_AdjustMenuItemRect(menu, &rect);
738 if (PtInRect(&rect, pt))
748 /***********************************************************************
751 * Find the menu item selected by a key press.
752 * Return item id, -1 if none, -2 if we should close the menu.
754 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
755 WCHAR key, BOOL forceMenuChar )
757 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
759 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
763 POPUPMENU *menu = MENU_GetMenu( hmenu );
764 MENUITEM *item = menu->items;
771 for (i = 0; i < menu->nItems; i++, item++)
775 WCHAR *p = item->text - 2;
778 p = strchrW (p + 2, '&');
780 while (p != NULL && p [1] == '&');
781 if (p && (toupperW(p[1]) == toupperW(key))) return i;
785 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
786 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
787 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
788 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
794 /***********************************************************************
795 * MENU_GetBitmapItemSize
797 * Get the size of a bitmap item.
799 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
803 HBITMAP bmp = lpitem->hbmpItem;
805 size->cx = size->cy = 0;
807 /* check if there is a magic menu item associated with this item */
808 switch( (INT_PTR) bmp )
810 case (INT_PTR)HBMMENU_CALLBACK:
812 MEASUREITEMSTRUCT measItem;
813 measItem.CtlType = ODT_MENU;
815 measItem.itemID = lpitem->wID;
816 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
817 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
818 measItem.itemData = lpitem->dwItemData;
819 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
820 size->cx = measItem.itemWidth;
821 size->cy = measItem.itemHeight;
825 case (INT_PTR)HBMMENU_SYSTEM:
826 if (lpitem->dwItemData)
828 bmp = (HBITMAP)lpitem->dwItemData;
832 case (INT_PTR)HBMMENU_MBAR_RESTORE:
833 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
834 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
835 case (INT_PTR)HBMMENU_MBAR_CLOSE:
836 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
837 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
840 case (INT_PTR)HBMMENU_POPUP_CLOSE:
841 case (INT_PTR)HBMMENU_POPUP_RESTORE:
842 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
843 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
844 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
845 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
848 if (GetObjectW(bmp, sizeof(bm), &bm ))
850 size->cx = bm.bmWidth;
851 size->cy = bm.bmHeight;
855 /***********************************************************************
856 * MENU_DrawBitmapItem
858 * Draw a bitmap item.
860 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
861 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
867 int w = rect->right - rect->left;
868 int h = rect->bottom - rect->top;
871 HBITMAP hbmToDraw = lpitem->hbmpItem;
874 /* Check if there is a magic menu item associated with this item */
875 if (IS_MAGIC_BITMAP(hbmToDraw))
881 switch((INT_PTR)hbmToDraw)
883 case (INT_PTR)HBMMENU_SYSTEM:
884 if (lpitem->dwItemData)
886 bmp = (HBITMAP)lpitem->dwItemData;
887 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
891 static HBITMAP hBmpSysMenu;
893 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
895 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
896 /* only use right half of the bitmap */
897 bmp_xoffset = bm.bmWidth / 2;
898 bm.bmWidth -= bmp_xoffset;
901 case (INT_PTR)HBMMENU_MBAR_RESTORE:
902 flags = DFCS_CAPTIONRESTORE;
904 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
905 flags = DFCS_CAPTIONMIN;
907 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
908 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
910 case (INT_PTR)HBMMENU_MBAR_CLOSE:
911 flags = DFCS_CAPTIONCLOSE;
913 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
914 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
916 case (INT_PTR)HBMMENU_CALLBACK:
918 DRAWITEMSTRUCT drawItem;
919 drawItem.CtlType = ODT_MENU;
921 drawItem.itemID = lpitem->wID;
922 drawItem.itemAction = odaction;
923 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
924 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
925 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
926 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
927 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
928 drawItem.hwndItem = (HWND)hmenu;
930 drawItem.itemData = lpitem->dwItemData;
931 drawItem.rcItem = *rect;
932 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
936 case (INT_PTR)HBMMENU_POPUP_CLOSE:
939 case (INT_PTR)HBMMENU_POPUP_RESTORE:
942 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
945 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
949 FIXME("Magic %p not implemented\n", hbmToDraw);
954 /* draw the magic bitmaps using marlett font characters */
955 /* FIXME: fontsize and the position (x,y) could probably be better */
956 HFONT hfont, hfontsav;
957 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
958 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
959 { 'M','a','r','l','e','t','t',0 } };
960 logfont.lfHeight = min( h, w) - 5 ;
961 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
962 hfont = CreateFontIndirectW( &logfont);
963 hfontsav = SelectObject(hdc, hfont);
964 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
965 SelectObject(hdc, hfontsav);
966 DeleteObject( hfont);
971 InflateRect( &r, -1, -1 );
972 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
973 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
978 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
981 hdcMem = CreateCompatibleDC( hdc );
982 SelectObject( hdcMem, bmp );
984 /* handle fontsize > bitmap_height */
985 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
987 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
988 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
989 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
990 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
995 /***********************************************************************
998 * Calculate the size of the menu item and store it in lpitem->rect.
1000 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1001 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1004 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1005 UINT arrow_bitmap_width;
1009 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1010 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1011 (menuBar ? " (MenuBar)" : ""));
1013 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1014 arrow_bitmap_width = bm.bmWidth;
1016 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1017 if( !menucharsize.cx ) {
1018 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1019 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1020 * but it is unlikely an application will depend on that */
1021 ODitemheight = HIWORD( GetDialogBaseUnits());
1024 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1026 if (lpitem->fType & MF_OWNERDRAW)
1028 MEASUREITEMSTRUCT mis;
1029 mis.CtlType = ODT_MENU;
1031 mis.itemID = lpitem->wID;
1032 mis.itemData = lpitem->dwItemData;
1033 mis.itemHeight = ODitemheight;
1035 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1036 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1037 * width of a menufont character to the width of an owner-drawn menu.
1039 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1041 /* under at least win95 you seem to be given a standard
1042 height for the menu and the height value is ignored */
1043 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1045 lpitem->rect.bottom += mis.itemHeight;
1047 TRACE("id=%04lx size=%dx%d\n",
1048 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1049 lpitem->rect.bottom-lpitem->rect.top);
1053 if (lpitem->fType & MF_SEPARATOR)
1055 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1057 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1065 if (lpitem->hbmpItem) {
1068 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1069 /* Keep the size of the bitmap in callback mode to be able
1070 * to draw it correctly */
1071 lpitem->bmpsize = size;
1072 lppop->textOffset = max( lppop->textOffset, size.cx);
1073 lpitem->rect.right += size.cx + 2;
1074 itemheight = size.cy + 2;
1076 if( !(lppop->dwStyle & MNS_NOCHECK))
1077 lpitem->rect.right += check_bitmap_width;
1078 lpitem->rect.right += 4 + menucharsize.cx;
1079 lpitem->xTab = lpitem->rect.right;
1080 lpitem->rect.right += arrow_bitmap_width;
1081 } else if (lpitem->hbmpItem) { /* menuBar */
1084 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1085 lpitem->bmpsize = size;
1086 lpitem->rect.right += size.cx;
1087 if( lpitem->text) lpitem->rect.right += 2;
1088 itemheight = size.cy;
1091 /* it must be a text item - unless it's the system menu */
1092 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1093 HFONT hfontOld = NULL;
1094 RECT rc = lpitem->rect;
1095 LONG txtheight, txtwidth;
1097 if ( lpitem->fState & MFS_DEFAULT ) {
1098 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1101 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1102 DT_SINGLELINE|DT_CALCRECT);
1103 lpitem->rect.right += rc.right - rc.left;
1104 itemheight = max( max( itemheight, txtheight),
1105 GetSystemMetrics( SM_CYMENU) - 1);
1106 lpitem->rect.right += 2 * menucharsize.cx;
1108 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1111 int n = (int)( p - lpitem->text);
1112 /* Item contains a tab (only meaningful in popup menus) */
1113 /* get text size before the tab */
1114 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1115 DT_SINGLELINE|DT_CALCRECT);
1116 txtwidth = rc.right - rc.left;
1117 p += 1; /* advance past the Tab */
1118 /* get text size after the tab */
1119 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1120 DT_SINGLELINE|DT_CALCRECT);
1121 lpitem->xTab += txtwidth;
1122 txtheight = max( txtheight, tmpheight);
1123 txtwidth += menucharsize.cx + /* space for the tab */
1124 tmprc.right - tmprc.left; /* space for the short cut */
1126 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1127 DT_SINGLELINE|DT_CALCRECT);
1128 txtwidth = rc.right - rc.left;
1129 lpitem->xTab += txtwidth;
1131 lpitem->rect.right += 2 + txtwidth;
1132 itemheight = max( itemheight,
1133 max( txtheight + 2, menucharsize.cy + 4));
1135 if (hfontOld) SelectObject (hdc, hfontOld);
1136 } else if( menuBar) {
1137 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1139 lpitem->rect.bottom += itemheight;
1140 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1144 /***********************************************************************
1145 * MENU_GetMaxPopupHeight
1148 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1151 return lppop->cyMax;
1152 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1156 /***********************************************************************
1157 * MENU_PopupMenuCalcSize
1159 * Calculate the size of a popup menu.
1161 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1166 int textandbmp = FALSE;
1167 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1169 lppop->Width = lppop->Height = 0;
1170 if (lppop->nItems == 0) return;
1173 SelectObject( hdc, get_menu_font(FALSE));
1178 lppop->textOffset = 0;
1180 while (start < lppop->nItems)
1182 lpitem = &lppop->items[start];
1184 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1185 orgX += MENU_COL_SPACE;
1186 orgY = MENU_TOP_MARGIN;
1188 maxTab = maxTabWidth = 0;
1189 /* Parse items until column break or end of menu */
1190 for (i = start; i < lppop->nItems; i++, lpitem++)
1193 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1195 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1196 maxX = max( maxX, lpitem->rect.right );
1197 orgY = lpitem->rect.bottom;
1198 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1200 maxTab = max( maxTab, lpitem->xTab );
1201 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1203 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1206 /* Finish the column (set all items to the largest width found) */
1207 maxX = max( maxX, maxTab + maxTabWidth );
1208 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1210 lpitem->rect.right = maxX;
1211 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1212 lpitem->xTab = maxTab;
1215 lppop->Height = max( lppop->Height, orgY );
1218 lppop->Width = maxX;
1219 /* if none of the items have both text and bitmap then
1220 * the text and bitmaps are all aligned on the left. If there is at
1221 * least one item with both text and bitmap then bitmaps are
1222 * on the left and texts left aligned with the right hand side
1224 if( !textandbmp) lppop->textOffset = 0;
1226 /* space for 3d border */
1227 lppop->Height += MENU_BOTTOM_MARGIN;
1230 /* Adjust popup height if it exceeds maximum */
1231 maxHeight = MENU_GetMaxPopupHeight(lppop);
1232 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1233 if (lppop->Height >= maxHeight)
1235 lppop->Height = maxHeight;
1236 lppop->bScrolling = TRUE;
1240 lppop->bScrolling = FALSE;
1243 ReleaseDC( 0, hdc );
1247 /***********************************************************************
1248 * MENU_MenuBarCalcSize
1250 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1251 * height is off by 1 pixel which causes lengthy window relocations when
1252 * active document window is maximized/restored.
1254 * Calculate the size of the menu bar.
1256 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1257 LPPOPUPMENU lppop, HWND hwndOwner )
1260 UINT start, i, helpPos;
1261 int orgX, orgY, maxY;
1263 if ((lprect == NULL) || (lppop == NULL)) return;
1264 if (lppop->nItems == 0) return;
1265 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1266 lppop->Width = lprect->right - lprect->left;
1268 maxY = lprect->top+1;
1271 lppop->textOffset = 0;
1272 while (start < lppop->nItems)
1274 lpitem = &lppop->items[start];
1275 orgX = lprect->left;
1278 /* Parse items until line break or end of menu */
1279 for (i = start; i < lppop->nItems; i++, lpitem++)
1281 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1283 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1285 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1286 debug_print_menuitem (" item: ", lpitem, "");
1287 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1289 if (lpitem->rect.right > lprect->right)
1291 if (i != start) break;
1292 else lpitem->rect.right = lprect->right;
1294 maxY = max( maxY, lpitem->rect.bottom );
1295 orgX = lpitem->rect.right;
1298 /* Finish the line (set all items to the largest height found) */
1299 while (start < i) lppop->items[start++].rect.bottom = maxY;
1302 lprect->bottom = maxY;
1303 lppop->Height = lprect->bottom - lprect->top;
1305 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1306 /* the last item (if several lines, only move the last line) */
1307 if (helpPos == ~0U) return;
1308 lpitem = &lppop->items[lppop->nItems-1];
1309 orgY = lpitem->rect.top;
1310 orgX = lprect->right;
1311 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1312 if (lpitem->rect.top != orgY) break; /* Other line */
1313 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1314 lpitem->rect.left += orgX - lpitem->rect.right;
1315 lpitem->rect.right = orgX;
1316 orgX = lpitem->rect.left;
1321 /***********************************************************************
1322 * MENU_DrawScrollArrows
1324 * Draw scroll arrows.
1327 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1329 HDC hdcMem = CreateCompatibleDC(hdc);
1330 HBITMAP hOrigBitmap;
1331 UINT arrow_bitmap_width, arrow_bitmap_height;
1335 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1336 arrow_bitmap_width = bmp.bmWidth;
1337 arrow_bitmap_height = bmp.bmHeight;
1340 if (lppop->nScrollPos)
1341 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1346 rect.right = lppop->Width;
1347 rect.bottom = arrow_bitmap_height;
1348 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1349 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1350 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1351 rect.top = lppop->Height - arrow_bitmap_height;
1352 rect.bottom = lppop->Height;
1353 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1354 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1355 SelectObject(hdcMem, get_down_arrow_bitmap());
1357 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1358 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1359 lppop->Height - arrow_bitmap_height,
1360 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1361 SelectObject(hdcMem, hOrigBitmap);
1366 /***********************************************************************
1369 * Draws the popup-menu arrow.
1371 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1372 UINT arrow_bitmap_height)
1374 HDC hdcMem = CreateCompatibleDC( hdc );
1375 HBITMAP hOrigBitmap;
1377 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1378 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1379 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1380 arrow_bitmap_width, arrow_bitmap_height,
1381 hdcMem, 0, 0, SRCCOPY );
1382 SelectObject( hdcMem, hOrigBitmap );
1385 /***********************************************************************
1388 * Draw a single menu item.
1390 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1391 UINT height, BOOL menuBar, UINT odaction )
1394 BOOL flat_menu = FALSE;
1396 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1397 POPUPMENU *menu = MENU_GetMenu(hmenu);
1400 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1404 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1405 arrow_bitmap_width = bmp.bmWidth;
1406 arrow_bitmap_height = bmp.bmHeight;
1409 if (lpitem->fType & MF_SYSMENU)
1411 if( !IsIconic(hwnd) )
1412 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1416 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1417 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1421 if (lpitem->fState & MF_HILITE)
1423 if(menuBar && !flat_menu) {
1424 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1425 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1427 if(lpitem->fState & MF_GRAYED)
1428 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1430 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1431 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1436 if (lpitem->fState & MF_GRAYED)
1437 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1439 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1440 SetBkColor( hdc, GetSysColor( bkgnd ) );
1443 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1444 rect = lpitem->rect;
1445 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1447 if (lpitem->fType & MF_OWNERDRAW)
1450 ** Experimentation under Windows reveals that an owner-drawn
1451 ** menu is given the rectangle which includes the space it requested
1452 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1453 ** and a popup-menu arrow. This is the value of lpitem->rect.
1454 ** Windows will leave all drawing to the application except for
1455 ** the popup-menu arrow. Windows always draws that itself, after
1456 ** the menu owner has finished drawing.
1460 dis.CtlType = ODT_MENU;
1462 dis.itemID = lpitem->wID;
1463 dis.itemData = lpitem->dwItemData;
1465 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1466 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1467 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1468 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1469 dis.hwndItem = (HWND)hmenu;
1472 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1473 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1474 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1475 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1476 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1477 /* Draw the popup-menu arrow */
1478 if (lpitem->fType & MF_POPUP)
1479 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1480 arrow_bitmap_height);
1484 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1486 if (lpitem->fState & MF_HILITE)
1490 InflateRect (&rect, -1, -1);
1491 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1492 InflateRect (&rect, 1, 1);
1493 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1498 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1500 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1504 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1506 SetBkMode( hdc, TRANSPARENT );
1508 /* vertical separator */
1509 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1514 rc.left -= MENU_COL_SPACE / 2 + 1;
1516 rc.bottom = height - 3;
1519 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1520 MoveToEx( hdc, rc.left, rc.top, NULL );
1521 LineTo( hdc, rc.left, rc.bottom );
1522 SelectObject( hdc, oldPen );
1525 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1528 /* horizontal separator */
1529 if (lpitem->fType & MF_SEPARATOR)
1536 rc.top = ( rc.top + rc.bottom) / 2;
1539 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1540 MoveToEx( hdc, rc.left, rc.top, NULL );
1541 LineTo( hdc, rc.right, rc.top );
1542 SelectObject( hdc, oldPen );
1545 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1549 /* helper lines for debugging */
1550 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1551 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1552 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1553 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1556 if (lpitem->hbmpItem) {
1557 /* calculate the bitmap rectangle in coordinates relative
1558 * to the item rectangle */
1560 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1563 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1565 else if (menu->dwStyle & MNS_NOCHECK)
1567 else if (menu->dwStyle & MNS_CHECKORBMP)
1570 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1571 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1572 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1575 bmprc.top = (rect.bottom - rect.top -
1576 lpitem->bmpsize.cy) / 2;
1577 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1583 INT y = rect.top + rect.bottom;
1585 int checked = FALSE;
1586 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1587 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1588 /* Draw the check mark
1591 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1593 if( !(menu->dwStyle & MNS_NOCHECK)) {
1594 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1595 lpitem->hUnCheckBit;
1596 if (bm) /* we have a custom bitmap */
1598 HDC hdcMem = CreateCompatibleDC( hdc );
1600 SelectObject( hdcMem, bm );
1601 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1602 check_bitmap_width, check_bitmap_height,
1603 hdcMem, 0, 0, SRCCOPY );
1607 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1610 HBITMAP bm = CreateBitmap( check_bitmap_width,
1611 check_bitmap_height, 1, 1, NULL );
1612 HDC hdcMem = CreateCompatibleDC( hdc );
1614 SelectObject( hdcMem, bm );
1615 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1616 DrawFrameControl( hdcMem, &r, DFC_MENU,
1617 (lpitem->fType & MFT_RADIOCHECK) ?
1618 DFCS_MENUBULLET : DFCS_MENUCHECK );
1619 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1620 hdcMem, 0, 0, SRCCOPY );
1626 if( lpitem->hbmpItem &&
1627 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1629 /* some applications make this assumption on the DC's origin */
1630 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1631 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1633 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1635 /* Draw the popup-menu arrow */
1636 if (lpitem->fType & MF_POPUP)
1637 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1638 arrow_bitmap_height);
1640 if( !(menu->dwStyle & MNS_NOCHECK))
1641 rect.left += check_bitmap_width;
1642 rect.right -= arrow_bitmap_width;
1644 else if( lpitem->hbmpItem)
1645 { /* Draw the bitmap */
1648 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1649 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1651 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1653 /* process text if present */
1659 UINT uFormat = (menuBar) ?
1660 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1661 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1663 if( !(menu->dwStyle & MNS_CHECKORBMP))
1664 rect.left += menu->textOffset;
1666 if ( lpitem->fState & MFS_DEFAULT )
1668 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1672 if( lpitem->hbmpItem)
1673 rect.left += lpitem->bmpsize.cx;
1674 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1675 rect.left += menucharsize.cx;
1676 rect.right -= menucharsize.cx;
1679 for (i = 0; lpitem->text[i]; i++)
1680 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1683 if(lpitem->fState & MF_GRAYED)
1685 if (!(lpitem->fState & MF_HILITE) )
1687 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1688 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1689 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1690 --rect.left; --rect.top; --rect.right; --rect.bottom;
1692 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1695 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1697 /* paint the shortcut text */
1698 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1700 if (lpitem->text[i] == '\t')
1702 rect.left = lpitem->xTab;
1703 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1707 rect.right = lpitem->xTab;
1708 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1711 if(lpitem->fState & MF_GRAYED)
1713 if (!(lpitem->fState & MF_HILITE) )
1715 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1716 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1717 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1718 --rect.left; --rect.top; --rect.right; --rect.bottom;
1720 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1722 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1726 SelectObject (hdc, hfontOld);
1731 /***********************************************************************
1732 * MENU_DrawPopupMenu
1734 * Paint a popup menu.
1736 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1738 HBRUSH hPrevBrush = 0;
1741 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1743 GetClientRect( hwnd, &rect );
1745 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1746 && (SelectObject( hdc, get_menu_font(FALSE))))
1750 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1752 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1756 BOOL flat_menu = FALSE;
1758 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1760 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1762 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1764 if( (menu = MENU_GetMenu( hmenu )))
1766 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1767 /* draw menu items */
1774 for( u = menu->nItems; u > 0; u--, item++)
1775 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1776 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1778 /* draw scroll arrows */
1779 if (menu->bScrolling)
1780 MENU_DrawScrollArrows(menu, hdc);
1784 SelectObject( hdc, hPrevBrush );
1789 /***********************************************************************
1792 * Paint a menu bar. Returns the height of the menu bar.
1793 * called from [windows/nonclient.c]
1795 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1800 HMENU hMenu = GetMenu(hwnd);
1802 lppop = MENU_GetMenu( hMenu );
1803 if (lppop == NULL || lprect == NULL)
1805 return GetSystemMetrics(SM_CYMENU);
1810 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1812 if (lppop->Height == 0)
1813 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1815 lprect->bottom = lprect->top + lppop->Height;
1817 if (hfontOld) SelectObject( hDC, hfontOld);
1818 return lppop->Height;
1821 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1825 /***********************************************************************
1828 * Display a popup menu.
1830 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1831 INT x, INT y, INT xanchor, INT yanchor )
1840 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1841 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1843 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1844 if (menu->FocusedItem != NO_SELECTED_ITEM)
1846 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1847 menu->FocusedItem = NO_SELECTED_ITEM;
1850 /* store the owner for DrawItem */
1851 if (!IsWindow( hwndOwner ))
1853 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1856 menu->hwndOwner = hwndOwner;
1858 menu->nScrollPos = 0;
1859 MENU_PopupMenuCalcSize( menu );
1861 /* adjust popup menu pos so that it fits within the desktop */
1863 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1864 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1866 /* FIXME: should use item rect */
1869 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1870 info.cbSize = sizeof(info);
1871 GetMonitorInfoW( monitor, &info );
1873 if (flags & TPM_LAYOUTRTL)
1875 ex_style = WS_EX_LAYOUTRTL;
1876 flags ^= TPM_RIGHTALIGN;
1879 if( flags & TPM_RIGHTALIGN ) x -= width;
1880 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1882 if( flags & TPM_BOTTOMALIGN ) y -= height;
1883 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1885 if( x + width > info.rcWork.right)
1887 if( xanchor && x >= width - xanchor )
1888 x -= width - xanchor;
1890 if( x + width > info.rcWork.right)
1891 x = info.rcWork.right - width;
1893 if( x < info.rcWork.left ) x = info.rcWork.left;
1895 if( y + height > info.rcWork.bottom)
1897 if( yanchor && y >= height + yanchor )
1898 y -= height + yanchor;
1900 if( y + height > info.rcWork.bottom)
1901 y = info.rcWork.bottom - height;
1903 if( y < info.rcWork.top ) y = info.rcWork.top;
1905 /* NOTE: In Windows, top menu popup is not owned. */
1906 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1907 WS_POPUP, x, y, width, height,
1908 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1910 if( !menu->hWnd ) return FALSE;
1912 top_popup = menu->hWnd;
1913 top_popup_hmenu = hmenu;
1915 /* Display the window */
1917 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1918 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1919 UpdateWindow( menu->hWnd );
1924 /***********************************************************************
1925 * MENU_EnsureMenuItemVisible
1928 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1930 if (lppop->bScrolling)
1932 MENUITEM *item = &lppop->items[wIndex];
1933 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1934 UINT nOldPos = lppop->nScrollPos;
1936 UINT arrow_bitmap_height;
1939 GetClientRect(lppop->hWnd, &rc);
1941 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1942 arrow_bitmap_height = bmp.bmHeight;
1944 rc.top += arrow_bitmap_height;
1945 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1947 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1948 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1951 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1952 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1953 MENU_DrawScrollArrows(lppop, hdc);
1955 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1957 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1958 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1959 MENU_DrawScrollArrows(lppop, hdc);
1965 /***********************************************************************
1968 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1969 BOOL sendMenuSelect, HMENU topmenu )
1974 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1976 lppop = MENU_GetMenu( hmenu );
1977 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1979 if (lppop->FocusedItem == wIndex) return;
1980 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1981 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1983 top_popup = lppop->hWnd;
1984 top_popup_hmenu = hmenu;
1987 SelectObject( hdc, get_menu_font(FALSE));
1989 /* Clear previous highlighted item */
1990 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1992 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1993 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1994 lppop->Height, !(lppop->wFlags & MF_POPUP),
1998 /* Highlight new item (if any) */
1999 lppop->FocusedItem = wIndex;
2000 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2002 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2003 lppop->items[wIndex].fState |= MF_HILITE;
2004 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2005 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2006 &lppop->items[wIndex], lppop->Height,
2007 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2011 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2012 SendMessageW( hwndOwner, WM_MENUSELECT,
2013 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2014 ip->fType | ip->fState |
2015 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2018 else if (sendMenuSelect) {
2021 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2022 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2023 MENUITEM *ip = &ptm->items[pos];
2024 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2025 ip->fType | ip->fState |
2026 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2030 ReleaseDC( lppop->hWnd, hdc );
2034 /***********************************************************************
2035 * MENU_MoveSelection
2037 * Moves currently selected item according to the offset parameter.
2038 * If there is no selection then it should select the last item if
2039 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2041 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2046 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2048 menu = MENU_GetMenu( hmenu );
2049 if ((!menu) || (!menu->items)) return;
2051 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2053 if( menu->nItems == 1 ) return; else
2054 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2056 if (!(menu->items[i].fType & MF_SEPARATOR))
2058 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2063 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2064 i >= 0 && i < menu->nItems ; i += offset)
2065 if (!(menu->items[i].fType & MF_SEPARATOR))
2067 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2073 /**********************************************************************
2076 * Insert (allocate) a new item into a menu.
2078 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2083 if (!(menu = MENU_GetMenu(hMenu)))
2086 /* Find where to insert new item */
2088 if (flags & MF_BYPOSITION) {
2089 if (pos > menu->nItems)
2092 if (!MENU_FindItem( &hMenu, &pos, flags ))
2095 if (!(menu = MENU_GetMenu( hMenu )))
2100 /* Make sure that MDI system buttons stay on the right side.
2101 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2102 * regardless of their id.
2104 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2105 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2108 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2110 /* Create new items array */
2112 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2115 WARN("allocation failed\n" );
2118 if (menu->nItems > 0)
2120 /* Copy the old array into the new one */
2121 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2122 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2123 (menu->nItems-pos)*sizeof(MENUITEM) );
2124 HeapFree( GetProcessHeap(), 0, menu->items );
2126 menu->items = newItems;
2128 memset( &newItems[pos], 0, sizeof(*newItems) );
2129 menu->Height = 0; /* force size recalculate */
2130 return &newItems[pos];
2134 /**********************************************************************
2135 * MENU_ParseResource
2137 * Parse a standard menu resource and add items to the menu.
2138 * Return a pointer to the end of the resource.
2140 * NOTE: flags is equivalent to the mtOption field
2142 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2150 flags = GET_WORD(res);
2151 end_flag = flags & MF_END;
2152 /* Remove MF_END because it has the same value as MF_HILITE */
2154 res += sizeof(WORD);
2155 if (!(flags & MF_POPUP))
2158 res += sizeof(WORD);
2161 res += (strlenW(str) + 1) * sizeof(WCHAR);
2162 if (flags & MF_POPUP)
2164 HMENU hSubMenu = CreatePopupMenu();
2165 if (!hSubMenu) return NULL;
2166 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2167 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2169 else /* Not a popup */
2171 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2173 } while (!end_flag);
2178 /**********************************************************************
2179 * MENUEX_ParseResource
2181 * Parse an extended menu resource and add items to the menu.
2182 * Return a pointer to the end of the resource.
2184 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2190 mii.cbSize = sizeof(mii);
2191 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2192 mii.fType = GET_DWORD(res);
2193 res += sizeof(DWORD);
2194 mii.fState = GET_DWORD(res);
2195 res += sizeof(DWORD);
2196 mii.wID = GET_DWORD(res);
2197 res += sizeof(DWORD);
2198 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2199 res += sizeof(WORD);
2200 /* Align the text on a word boundary. */
2201 res += (~((UINT_PTR)res - 1)) & 1;
2202 mii.dwTypeData = (LPWSTR) res;
2203 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2204 /* Align the following fields on a dword boundary. */
2205 res += (~((UINT_PTR)res - 1)) & 3;
2207 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2208 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2210 if (resinfo & 1) { /* Pop-up? */
2211 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2212 res += sizeof(DWORD);
2213 mii.hSubMenu = CreatePopupMenu();
2216 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2217 DestroyMenu(mii.hSubMenu);
2220 mii.fMask |= MIIM_SUBMENU;
2221 mii.fType |= MF_POPUP;
2223 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2225 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2226 mii.wID, mii.fType);
2227 mii.fType |= MF_SEPARATOR;
2229 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2230 } while (!(resinfo & MF_END));
2235 /***********************************************************************
2238 * Return the handle of the selected sub-popup menu (if any).
2240 static HMENU MENU_GetSubPopup( HMENU hmenu )
2245 menu = MENU_GetMenu( hmenu );
2247 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2249 item = &menu->items[menu->FocusedItem];
2250 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2251 return item->hSubMenu;
2256 /***********************************************************************
2257 * MENU_HideSubPopups
2259 * Hide the sub-popup menus of this menu.
2261 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2262 BOOL sendMenuSelect, UINT wFlags )
2264 POPUPMENU *menu = MENU_GetMenu( hmenu );
2266 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2268 if (menu && top_popup)
2274 if (menu->FocusedItem != NO_SELECTED_ITEM)
2276 item = &menu->items[menu->FocusedItem];
2277 if (!(item->fType & MF_POPUP) ||
2278 !(item->fState & MF_MOUSESELECT)) return;
2279 item->fState &= ~MF_MOUSESELECT;
2280 hsubmenu = item->hSubMenu;
2283 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2284 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2285 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2286 DestroyWindow( submenu->hWnd );
2289 if (!(wFlags & TPM_NONOTIFY))
2290 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2291 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2296 /***********************************************************************
2299 * Display the sub-menu of the selected item of this menu.
2300 * Return the handle of the submenu, or hmenu if no submenu to display.
2302 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2303 BOOL selectFirst, UINT wFlags )
2310 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2312 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2314 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2316 item = &menu->items[menu->FocusedItem];
2317 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2320 /* message must be sent before using item,
2321 because nearly everything may be changed by the application ! */
2323 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2324 if (!(wFlags & TPM_NONOTIFY))
2325 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2326 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2328 item = &menu->items[menu->FocusedItem];
2331 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2332 if (!(item->fState & MF_HILITE))
2334 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2335 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2337 SelectObject( hdc, get_menu_font(FALSE));
2339 item->fState |= MF_HILITE;
2340 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2341 ReleaseDC( menu->hWnd, hdc );
2343 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2346 item->fState |= MF_MOUSESELECT;
2348 if (IS_SYSTEM_MENU(menu))
2350 MENU_InitSysMenuPopup(item->hSubMenu,
2351 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2352 GetClassLongW( menu->hWnd, GCL_STYLE));
2354 NC_GetSysPopupPos( menu->hWnd, &rect );
2355 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2356 rect.top = rect.bottom;
2357 rect.right = GetSystemMetrics(SM_CXSIZE);
2358 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2362 GetWindowRect( menu->hWnd, &rect );
2363 if (menu->wFlags & MF_POPUP)
2365 RECT rc = item->rect;
2367 MENU_AdjustMenuItemRect(menu, &rc);
2369 /* The first item in the popup menu has to be at the
2370 same y position as the focused menu item */
2371 if (wFlags & TPM_LAYOUTRTL)
2372 rect.left += GetSystemMetrics(SM_CXBORDER);
2374 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2375 rect.top += rc.top - MENU_TOP_MARGIN;
2376 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2377 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2378 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2382 if (wFlags & TPM_LAYOUTRTL)
2383 rect.left = rect.right - item->rect.left;
2385 rect.left += item->rect.left;
2386 rect.top += item->rect.bottom;
2387 rect.right = item->rect.right - item->rect.left;
2388 rect.bottom = item->rect.bottom - item->rect.top;
2392 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2393 rect.left, rect.top, rect.right, rect.bottom );
2395 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2396 return item->hSubMenu;
2401 /**********************************************************************
2404 HWND MENU_IsMenuActive(void)
2409 /**********************************************************************
2412 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2414 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2416 void MENU_EndMenu( HWND hwnd )
2419 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2420 if (menu && hwnd == menu->hwndOwner) EndMenu();
2423 /***********************************************************************
2426 * Walks menu chain trying to find a menu pt maps to.
2428 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2430 POPUPMENU *menu = MENU_GetMenu( hMenu );
2431 UINT item = menu->FocusedItem;
2434 /* try subpopup first (if any) */
2435 ret = (item != NO_SELECTED_ITEM &&
2436 (menu->items[item].fType & MF_POPUP) &&
2437 (menu->items[item].fState & MF_MOUSESELECT))
2438 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2440 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2442 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2443 if( menu->wFlags & MF_POPUP )
2445 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2447 else if (ht == HTSYSMENU)
2448 ret = get_win_sys_menu( menu->hWnd );
2449 else if (ht == HTMENU)
2450 ret = GetMenu( menu->hWnd );
2455 /***********************************************************************
2456 * MENU_ExecFocusedItem
2458 * Execute a menu item (for instance when user pressed Enter).
2459 * Return the wID of the executed item. Otherwise, -1 indicating
2460 * that no menu item was executed, -2 if a popup is shown;
2461 * Have to receive the flags for the TrackPopupMenu options to avoid
2462 * sending unwanted message.
2465 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2468 POPUPMENU *menu = MENU_GetMenu( hMenu );
2470 TRACE("%p hmenu=%p\n", pmt, hMenu);
2472 if (!menu || !menu->nItems ||
2473 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2475 item = &menu->items[menu->FocusedItem];
2477 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2479 if (!(item->fType & MF_POPUP))
2481 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2483 /* If TPM_RETURNCMD is set you return the id, but
2484 do not send a message to the owner */
2485 if(!(wFlags & TPM_RETURNCMD))
2487 if( menu->wFlags & MF_SYSMENU )
2488 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2489 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2492 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2493 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2495 if (dwStyle & MNS_NOTIFYBYPOS)
2496 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2499 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2507 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2514 /***********************************************************************
2515 * MENU_SwitchTracking
2517 * Helper function for menu navigation routines.
2519 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2521 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2522 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2524 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2526 if( pmt->hTopMenu != hPtMenu &&
2527 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2529 /* both are top level menus (system and menu-bar) */
2530 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2531 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2532 pmt->hTopMenu = hPtMenu;
2534 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2535 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2539 /***********************************************************************
2542 * Return TRUE if we can go on with menu tracking.
2544 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2546 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2551 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2554 if( IS_SYSTEM_MENU(ptmenu) )
2555 item = ptmenu->items;
2557 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2561 if( ptmenu->FocusedItem != id )
2562 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2564 /* If the popup menu is not already "popped" */
2565 if(!(item->fState & MF_MOUSESELECT ))
2567 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2572 /* Else the click was on the menu bar, finish the tracking */
2577 /***********************************************************************
2580 * Return the value of MENU_ExecFocusedItem if
2581 * the selected item was not a popup. Else open the popup.
2582 * A -1 return value indicates that we go on with menu tracking.
2585 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2587 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2592 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2595 if( IS_SYSTEM_MENU(ptmenu) )
2596 item = ptmenu->items;
2598 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2600 if( item && (ptmenu->FocusedItem == id ))
2602 debug_print_menuitem ("FocusedItem: ", item, "");
2604 if( !(item->fType & MF_POPUP) )
2606 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2607 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2608 return executedMenuId;
2611 /* If we are dealing with the top-level menu */
2612 /* and this is a click on an already "popped" item: */
2613 /* Stop the menu tracking and close the opened submenus */
2614 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2617 ptmenu->bTimeToHide = TRUE;
2623 /***********************************************************************
2626 * Return TRUE if we can go on with menu tracking.
2628 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2630 UINT id = NO_SELECTED_ITEM;
2631 POPUPMENU *ptmenu = NULL;
2635 ptmenu = MENU_GetMenu( hPtMenu );
2636 if( IS_SYSTEM_MENU(ptmenu) )
2639 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2642 if( id == NO_SELECTED_ITEM )
2644 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2645 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2648 else if( ptmenu->FocusedItem != id )
2650 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2651 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2657 /***********************************************************************
2660 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2662 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2664 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2667 /* When skipping left, we need to do something special after the
2669 if (vk == VK_LEFT && menu->FocusedItem == 0)
2673 /* When skipping right, for the non-system menu, we need to
2674 handle the last non-special menu item (ie skip any window
2675 icons such as MDI maximize, restore or close) */
2676 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2678 UINT i = menu->FocusedItem + 1;
2679 while (i < menu->nItems) {
2680 if ((menu->items[i].wID >= SC_SIZE &&
2681 menu->items[i].wID <= SC_RESTORE)) {
2685 if (i == menu->nItems) {
2689 /* When skipping right, we need to cater for the system menu */
2690 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2692 if (menu->FocusedItem == (menu->nItems - 1)) {
2699 MDINEXTMENU next_menu;
2704 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2705 next_menu.hmenuNext = 0;
2706 next_menu.hwndNext = 0;
2707 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2709 TRACE("%p [%p] -> %p [%p]\n",
2710 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2712 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2714 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2715 hNewWnd = pmt->hOwnerWnd;
2716 if( IS_SYSTEM_MENU(menu) )
2718 /* switch to the menu bar */
2720 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2724 menu = MENU_GetMenu( hNewMenu );
2725 id = menu->nItems - 1;
2727 /* Skip backwards over any system predefined icons,
2728 eg. MDI close, restore etc icons */
2730 (menu->items[id].wID >= SC_SIZE &&
2731 menu->items[id].wID <= SC_RESTORE)) id--;
2734 else if (style & WS_SYSMENU )
2736 /* switch to the system menu */
2737 hNewMenu = get_win_sys_menu( hNewWnd );
2741 else /* application returned a new menu to switch to */
2743 hNewMenu = next_menu.hmenuNext;
2744 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2746 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2748 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2750 if (style & WS_SYSMENU &&
2751 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2753 /* get the real system menu */
2754 hNewMenu = get_win_sys_menu(hNewWnd);
2756 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2758 /* FIXME: Not sure what to do here;
2759 * perhaps try to track hNewMenu as a popup? */
2761 TRACE(" -- got confused.\n");
2768 if( hNewMenu != pmt->hTopMenu )
2770 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2772 if( pmt->hCurrentMenu != pmt->hTopMenu )
2773 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2776 if( hNewWnd != pmt->hOwnerWnd )
2778 pmt->hOwnerWnd = hNewWnd;
2779 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2782 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2783 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2790 /***********************************************************************
2793 * The idea is not to show the popup if the next input message is
2794 * going to hide it anyway.
2796 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2800 msg.hwnd = pmt->hOwnerWnd;
2802 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2803 pmt->trackFlags |= TF_SKIPREMOVE;
2808 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2809 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2811 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2812 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2813 if( msg.message == WM_KEYDOWN &&
2814 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2816 pmt->trackFlags |= TF_SUSPENDPOPUP;
2823 /* failures go through this */
2824 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2828 /***********************************************************************
2831 * Handle a VK_ESCAPE key event in a menu.
2833 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2835 BOOL bEndMenu = TRUE;
2837 if (pmt->hCurrentMenu != pmt->hTopMenu)
2839 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2841 if (menu->wFlags & MF_POPUP)
2843 HMENU hmenutmp, hmenuprev;
2845 hmenuprev = hmenutmp = pmt->hTopMenu;
2847 /* close topmost popup */
2848 while (hmenutmp != pmt->hCurrentMenu)
2850 hmenuprev = hmenutmp;
2851 hmenutmp = MENU_GetSubPopup( hmenuprev );
2854 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2855 pmt->hCurrentMenu = hmenuprev;
2863 /***********************************************************************
2866 * Handle a VK_LEFT key event in a menu.
2868 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2871 HMENU hmenutmp, hmenuprev;
2874 hmenuprev = hmenutmp = pmt->hTopMenu;
2875 menu = MENU_GetMenu( hmenutmp );
2877 /* Try to move 1 column left (if possible) */
2878 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2879 NO_SELECTED_ITEM ) {
2881 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2886 /* close topmost popup */
2887 while (hmenutmp != pmt->hCurrentMenu)
2889 hmenuprev = hmenutmp;
2890 hmenutmp = MENU_GetSubPopup( hmenuprev );
2893 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2894 pmt->hCurrentMenu = hmenuprev;
2896 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2898 /* move menu bar selection if no more popups are left */
2900 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2901 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2903 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2905 /* A sublevel menu was displayed - display the next one
2906 * unless there is another displacement coming up */
2908 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2909 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2910 pmt->hTopMenu, TRUE, wFlags);
2916 /***********************************************************************
2919 * Handle a VK_RIGHT key event in a menu.
2921 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2924 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2927 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2929 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2930 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2932 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2934 /* If already displaying a popup, try to display sub-popup */
2936 hmenutmp = pmt->hCurrentMenu;
2937 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2939 /* if subpopup was displayed then we are done */
2940 if (hmenutmp != pmt->hCurrentMenu) return;
2943 /* Check to see if there's another column */
2944 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2945 NO_SELECTED_ITEM ) {
2946 TRACE("Going to %d.\n", nextcol );
2947 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2952 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2954 if( pmt->hCurrentMenu != pmt->hTopMenu )
2956 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2957 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2958 } else hmenutmp = 0;
2960 /* try to move to the next item */
2961 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2962 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2964 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2965 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2966 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2967 pmt->hTopMenu, TRUE, wFlags);
2971 static void CALLBACK release_capture( BOOL __normal )
2973 set_capture_window( 0, GUI_INMENUMODE, NULL );
2976 /***********************************************************************
2979 * Menu tracking code.
2981 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2982 HWND hwnd, const RECT *lprect )
2987 INT executedMenuId = -1;
2989 BOOL enterIdleSent = FALSE;
2993 mt.hCurrentMenu = hmenu;
2994 mt.hTopMenu = hmenu;
2995 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2999 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3000 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3003 if (!(menu = MENU_GetMenu( hmenu )))
3005 WARN("Invalid menu handle %p\n", hmenu);
3006 SetLastError(ERROR_INVALID_MENU_HANDLE);
3010 if (wFlags & TPM_BUTTONDOWN)
3012 /* Get the result in order to start the tracking or not */
3013 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3014 fEndMenu = !fRemove;
3017 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3019 /* owner may not be visible when tracking a popup, so use the menu itself */
3020 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3021 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3023 __TRY while (!fEndMenu)
3025 menu = MENU_GetMenu( mt.hCurrentMenu );
3026 if (!menu) /* sometimes happens if I do a window manager close */
3029 /* we have to keep the message in the queue until it's
3030 * clear that menu loop is not over yet. */
3034 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3036 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3037 /* remove the message from the queue */
3038 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3044 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3045 enterIdleSent = TRUE;
3046 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3052 /* check if EndMenu() tried to cancel us, by posting this message */
3053 if(msg.message == WM_CANCELMODE)
3055 /* we are now out of the loop */
3058 /* remove the message from the queue */
3059 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3061 /* break out of internal loop, ala ESCAPE */
3065 TranslateMessage( &msg );
3068 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3069 enterIdleSent=FALSE;
3072 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3075 * Use the mouse coordinates in lParam instead of those in the MSG
3076 * struct to properly handle synthetic messages. They are already
3077 * in screen coordinates.
3079 mt.pt.x = (short)LOWORD(msg.lParam);
3080 mt.pt.y = (short)HIWORD(msg.lParam);
3082 /* Find a menu for this mouse event */
3083 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3087 /* no WM_NC... messages in captured state */
3089 case WM_RBUTTONDBLCLK:
3090 case WM_RBUTTONDOWN:
3091 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3093 case WM_LBUTTONDBLCLK:
3094 case WM_LBUTTONDOWN:
3095 /* If the message belongs to the menu, removes it from the queue */
3096 /* Else, end menu tracking */
3097 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3098 fEndMenu = !fRemove;
3102 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3105 /* Check if a menu was selected by the mouse */
3108 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3109 TRACE("executedMenuId %d\n", executedMenuId);
3111 /* End the loop if executedMenuId is an item ID */
3112 /* or if the job was done (executedMenuId = 0). */
3113 fEndMenu = fRemove = (executedMenuId != -1);
3115 /* No menu was selected by the mouse */
3116 /* if the function was called by TrackPopupMenu, continue
3117 with the menu tracking. If not, stop it */
3119 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3124 /* the selected menu item must be changed every time */
3125 /* the mouse moves. */
3128 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3130 } /* switch(msg.message) - mouse */
3132 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3134 fRemove = TRUE; /* Keyboard messages are always removed */
3148 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3149 NO_SELECTED_ITEM, FALSE, 0 );
3150 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3151 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3155 case VK_DOWN: /* If on menu bar, pull-down the menu */
3157 menu = MENU_GetMenu( mt.hCurrentMenu );
3158 if (!(menu->wFlags & MF_POPUP))
3159 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3160 else /* otherwise try to move selection */
3161 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3162 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3166 MENU_KeyLeft( &mt, wFlags );
3170 MENU_KeyRight( &mt, wFlags );
3174 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3180 hi.cbSize = sizeof(HELPINFO);
3181 hi.iContextType = HELPINFO_MENUITEM;
3182 if (menu->FocusedItem == NO_SELECTED_ITEM)
3185 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3186 hi.hItemHandle = hmenu;
3187 hi.dwContextId = menu->dwContextHelpID;
3188 hi.MousePos = msg.pt;
3189 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3196 break; /* WM_KEYDOWN */
3203 if (msg.wParam == '\r' || msg.wParam == ' ')
3205 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3206 fEndMenu = (executedMenuId != -2);
3211 /* Hack to avoid control chars. */
3212 /* We will find a better way real soon... */
3213 if (msg.wParam < 32) break;
3215 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3216 LOWORD(msg.wParam), FALSE );
3217 if (pos == (UINT)-2) fEndMenu = TRUE;
3218 else if (pos == (UINT)-1) MessageBeep(0);
3221 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3223 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3224 fEndMenu = (executedMenuId != -2);
3228 } /* switch(msg.message) - kbd */
3232 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3233 DispatchMessageW( &msg );
3237 if (!fEndMenu) fRemove = TRUE;
3239 /* finally remove message from the queue */
3241 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3242 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3243 else mt.trackFlags &= ~TF_SKIPREMOVE;
3245 __FINALLY( release_capture )
3247 /* If dropdown is still painted and the close box is clicked on
3248 then the menu will be destroyed as part of the DispatchMessage above.
3249 This will then invalidate the menu handle in mt.hTopMenu. We should
3250 check for this first. */
3251 if( IsMenu( mt.hTopMenu ) )
3253 menu = MENU_GetMenu( mt.hTopMenu );
3255 if( IsWindow( mt.hOwnerWnd ) )
3257 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3259 if (menu && (menu->wFlags & MF_POPUP))
3261 DestroyWindow( menu->hWnd );
3264 if (!(wFlags & TPM_NONOTIFY))
3265 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3266 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3268 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3269 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3272 /* Reset the variable for hiding menu */
3273 if( menu ) menu->bTimeToHide = FALSE;
3276 /* The return value is only used by TrackPopupMenu */
3277 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3278 if (executedMenuId == -1) executedMenuId = 0;
3279 return executedMenuId;
3282 /***********************************************************************
3285 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3289 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3293 /* This makes the menus of applications built with Delphi work.
3294 * It also enables menus to be displayed in more than one window,
3295 * but there are some bugs left that need to be fixed in this case.
3297 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3299 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3300 if (!(wFlags & TPM_NONOTIFY))
3301 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3303 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3305 if (!(wFlags & TPM_NONOTIFY))
3307 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3308 /* If an app changed/recreated menu bar entries in WM_INITMENU
3309 * menu sizes will be recalculated once the menu created/shown.
3316 /***********************************************************************
3319 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3321 TRACE("hwnd=%p\n", hWnd);
3323 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3326 top_popup_hmenu = NULL;
3330 /***********************************************************************
3331 * MENU_TrackMouseMenuBar
3333 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3335 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3337 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3338 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3340 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3342 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3345 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3346 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3347 MENU_ExitTracking(hWnd, FALSE);
3352 /***********************************************************************
3353 * MENU_TrackKbdMenuBar
3355 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3357 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3359 UINT uItem = NO_SELECTED_ITEM;
3361 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3363 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3365 /* find window that has a menu */
3367 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3368 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3370 /* check if we have to track a system menu */
3372 hTrackMenu = GetMenu( hwnd );
3373 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3375 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3376 hTrackMenu = get_win_sys_menu( hwnd );
3378 wParam |= HTSYSMENU; /* prevent item lookup */
3380 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3382 if (!IsMenu( hTrackMenu )) return;
3384 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3386 if( wChar && wChar != ' ' )
3388 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3389 if ( uItem >= (UINT)(-2) )
3391 if( uItem == (UINT)(-1) ) MessageBeep(0);
3392 /* schedule end of menu tracking */
3393 wFlags |= TF_ENDMENU;
3398 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3400 if (!(wParam & HTSYSMENU) || wChar == ' ')
3402 if( uItem == NO_SELECTED_ITEM )
3403 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3405 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3409 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3410 MENU_ExitTracking( hwnd, FALSE );
3413 /**********************************************************************
3414 * TrackPopupMenuEx (USER32.@)
3416 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3417 HWND hWnd, LPTPMPARAMS lpTpm )
3422 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3423 hMenu, wFlags, x, y, hWnd, lpTpm,
3424 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3426 /* Parameter check */
3427 /* FIXME: this check is performed several times, here and in the called
3428 functions. That could be optimized */
3429 if (!(menu = MENU_GetMenu( hMenu )))
3431 SetLastError( ERROR_INVALID_MENU_HANDLE );
3435 if (IsWindow(menu->hWnd))
3437 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3441 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3443 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3444 if (!(wFlags & TPM_NONOTIFY))
3445 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3447 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3448 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3449 lpTpm ? &lpTpm->rcExclude : NULL );
3450 MENU_ExitTracking(hWnd, TRUE);
3455 /**********************************************************************
3456 * TrackPopupMenu (USER32.@)
3458 * Like the win32 API, the function return the command ID only if the
3459 * flag TPM_RETURNCMD is on.
3462 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3463 INT nReserved, HWND hWnd, const RECT *lpRect )
3465 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3468 /***********************************************************************
3471 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3473 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3475 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3481 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3482 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3486 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3487 return MA_NOACTIVATE;
3492 BeginPaint( hwnd, &ps );
3493 MENU_DrawPopupMenu( hwnd, ps.hdc,
3494 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3495 EndPaint( hwnd, &ps );
3499 case WM_PRINTCLIENT:
3501 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3502 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3510 /* zero out global pointer in case resident popup window was destroyed. */
3511 if (hwnd == top_popup) {
3513 top_popup_hmenu = NULL;
3521 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3524 SetWindowLongPtrW( hwnd, 0, 0 );
3527 case MM_SETMENUHANDLE:
3528 SetWindowLongPtrW( hwnd, 0, wParam );
3531 case MM_GETMENUHANDLE:
3533 return GetWindowLongPtrW( hwnd, 0 );
3536 return DefWindowProcW( hwnd, message, wParam, lParam );
3542 /***********************************************************************
3543 * MENU_GetMenuBarHeight
3545 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3547 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3548 INT orgX, INT orgY )
3554 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3556 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3558 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3559 SelectObject( hdc, get_menu_font(FALSE));
3560 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3561 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3562 ReleaseDC( hwnd, hdc );
3563 return lppop->Height;
3567 /*******************************************************************
3568 * ChangeMenuA (USER32.@)
3570 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3571 UINT id, UINT flags )
3573 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3574 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3576 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3577 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3579 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3580 flags & MF_BYPOSITION ? pos : id,
3581 flags & ~MF_REMOVE );
3582 /* Default: MF_INSERT */
3583 return InsertMenuA( hMenu, pos, flags, id, data );
3587 /*******************************************************************
3588 * ChangeMenuW (USER32.@)
3590 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3591 UINT id, UINT flags )
3593 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3594 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3596 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3597 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3599 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3600 flags & MF_BYPOSITION ? pos : id,
3601 flags & ~MF_REMOVE );
3602 /* Default: MF_INSERT */
3603 return InsertMenuW( hMenu, pos, flags, id, data );
3607 /*******************************************************************
3608 * CheckMenuItem (USER32.@)
3610 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3615 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3616 ret = item->fState & MF_CHECKED;
3617 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3618 else item->fState &= ~MF_CHECKED;
3623 /**********************************************************************
3624 * EnableMenuItem (USER32.@)
3626 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3632 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3634 /* Get the Popupmenu to access the owner menu */
3635 if (!(menu = MENU_GetMenu(hMenu)))
3638 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3641 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3642 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3644 /* If the close item in the system menu change update the close button */
3645 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3647 if (menu->hSysMenuOwner != 0)
3650 POPUPMENU* parentMenu;
3652 /* Get the parent menu to access*/
3653 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3656 /* Refresh the frame to reflect the change */
3657 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3659 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3667 /*******************************************************************
3668 * GetMenuStringA (USER32.@)
3670 INT WINAPI GetMenuStringA(
3671 HMENU hMenu, /* [in] menuhandle */
3672 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3673 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3674 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3675 UINT wFlags /* [in] MF_ flags */
3679 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3680 if (str && nMaxSiz) str[0] = '\0';
3681 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3682 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3685 if (!item->text) return 0;
3686 if (!str || !nMaxSiz) return strlenW(item->text);
3687 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3689 TRACE("returning %s\n", debugstr_a(str));
3694 /*******************************************************************
3695 * GetMenuStringW (USER32.@)
3697 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3698 LPWSTR str, INT nMaxSiz, UINT wFlags )
3702 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3703 if (str && nMaxSiz) str[0] = '\0';
3704 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3705 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3708 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3709 if( !(item->text)) {
3713 lstrcpynW( str, item->text, nMaxSiz );
3714 TRACE("returning %s\n", debugstr_w(str));
3715 return strlenW(str);
3719 /**********************************************************************
3720 * HiliteMenuItem (USER32.@)
3722 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3726 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3727 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3728 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3729 if (menu->FocusedItem == wItemID) return TRUE;
3730 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3731 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3736 /**********************************************************************
3737 * GetMenuState (USER32.@)
3739 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3742 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3743 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3744 debug_print_menuitem (" item: ", item, "");
3745 if (item->fType & MF_POPUP)
3747 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3748 if (!menu) return -1;
3749 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3753 /* We used to (from way back then) mask the result to 0xff. */
3754 /* I don't know why and it seems wrong as the documented */
3755 /* return flag MF_SEPARATOR is outside that mask. */
3756 return (item->fType | item->fState);
3761 /**********************************************************************
3762 * GetMenuItemCount (USER32.@)
3764 INT WINAPI GetMenuItemCount( HMENU hMenu )
3766 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3767 if (!menu) return -1;
3768 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3769 return menu->nItems;
3773 /**********************************************************************
3774 * GetMenuItemID (USER32.@)
3776 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3780 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3781 if (lpmi->fType & MF_POPUP) return -1;
3787 /**********************************************************************
3790 * Uses flags, id and text ptr, passed by InsertMenu() and
3791 * ModifyMenu() to setup a MenuItemInfo structure.
3793 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3794 LPMENUITEMINFOW pmii)
3796 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3797 pmii->cbSize = sizeof( MENUITEMINFOW);
3798 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3799 /* setting bitmap clears text and vice versa */
3800 if( IS_STRING_ITEM(flags)) {
3801 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3803 flags |= MF_SEPARATOR;
3804 /* Item beginning with a backspace is a help item */
3805 /* FIXME: wrong place, this is only true in win16 */
3806 else if( *str == '\b') {
3810 pmii->dwTypeData = (LPWSTR)str;
3811 } else if( flags & MFT_BITMAP){
3812 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3813 pmii->hbmpItem = ULongToHandle(LOWORD(str));
3815 if( flags & MF_OWNERDRAW){
3816 pmii->fMask |= MIIM_DATA;
3817 pmii->dwItemData = (ULONG_PTR) str;
3819 if( flags & MF_POPUP) {
3820 pmii->fMask |= MIIM_SUBMENU;
3821 pmii->hSubMenu = (HMENU)id;
3823 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3824 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3825 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3826 pmii->wID = (UINT)id;
3830 /*******************************************************************
3831 * InsertMenuW (USER32.@)
3833 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3834 UINT_PTR id, LPCWSTR str )
3839 if (IS_STRING_ITEM(flags) && str)
3840 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3841 hMenu, pos, flags, id, debugstr_w(str) );
3842 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3843 hMenu, pos, flags, id, str );
3845 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3846 MENU_mnu2mnuii( flags, id, str, &mii);
3847 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3849 RemoveMenu( hMenu, pos, flags );
3853 item->hCheckBit = item->hUnCheckBit = 0;
3858 /*******************************************************************
3859 * InsertMenuA (USER32.@)
3861 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3862 UINT_PTR id, LPCSTR str )
3866 if (IS_STRING_ITEM(flags) && str)
3868 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3869 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3872 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3873 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3874 HeapFree( GetProcessHeap(), 0, newstr );
3878 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3882 /*******************************************************************
3883 * AppendMenuA (USER32.@)
3885 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3886 UINT_PTR id, LPCSTR data )
3888 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3892 /*******************************************************************
3893 * AppendMenuW (USER32.@)
3895 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3896 UINT_PTR id, LPCWSTR data )
3898 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3902 /**********************************************************************
3903 * RemoveMenu (USER32.@)
3905 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3910 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3911 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3912 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3916 MENU_FreeItemData( item );
3918 if (--menu->nItems == 0)
3920 HeapFree( GetProcessHeap(), 0, menu->items );
3925 while(nPos < menu->nItems)
3931 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3932 menu->nItems * sizeof(MENUITEM) );
3938 /**********************************************************************
3939 * DeleteMenu (USER32.@)
3941 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3943 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3944 if (!item) return FALSE;
3945 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3946 /* nPos is now the position of the item */
3947 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3952 /*******************************************************************
3953 * ModifyMenuW (USER32.@)
3955 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3956 UINT_PTR id, LPCWSTR str )
3961 if (IS_STRING_ITEM(flags))
3962 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3964 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3966 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3967 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3968 MENU_mnu2mnuii( flags, id, str, &mii);
3969 return SetMenuItemInfo_common( item, &mii, TRUE);
3973 /*******************************************************************
3974 * ModifyMenuA (USER32.@)
3976 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3977 UINT_PTR id, LPCSTR str )
3981 if (IS_STRING_ITEM(flags) && str)
3983 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3984 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3987 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3988 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3989 HeapFree( GetProcessHeap(), 0, newstr );
3993 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3997 /**********************************************************************
3998 * CreatePopupMenu (USER32.@)
4000 HMENU WINAPI CreatePopupMenu(void)
4005 if (!(hmenu = CreateMenu())) return 0;
4006 menu = MENU_GetMenu( hmenu );
4007 menu->wFlags |= MF_POPUP;
4008 menu->bTimeToHide = FALSE;
4013 /**********************************************************************
4014 * GetMenuCheckMarkDimensions (USER.417)
4015 * GetMenuCheckMarkDimensions (USER32.@)
4017 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4019 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4023 /**********************************************************************
4024 * SetMenuItemBitmaps (USER32.@)
4026 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4027 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4031 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4033 if (!hNewCheck && !hNewUnCheck)
4035 item->fState &= ~MF_USECHECKBITMAPS;
4037 else /* Install new bitmaps */
4039 item->hCheckBit = hNewCheck;
4040 item->hUnCheckBit = hNewUnCheck;
4041 item->fState |= MF_USECHECKBITMAPS;
4047 /**********************************************************************
4048 * CreateMenu (USER32.@)
4050 HMENU WINAPI CreateMenu(void)
4055 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4056 menu->FocusedItem = NO_SELECTED_ITEM;
4057 menu->bTimeToHide = FALSE;
4059 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4061 TRACE("return %p\n", hMenu );
4067 /**********************************************************************
4068 * DestroyMenu (USER32.@)
4070 BOOL WINAPI DestroyMenu( HMENU hMenu )
4074 TRACE("(%p)\n", hMenu);
4076 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4077 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4079 /* DestroyMenu should not destroy system menu popup owner */
4080 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4082 DestroyWindow( lppop->hWnd );
4086 if (lppop->items) /* recursively destroy submenus */
4089 MENUITEM *item = lppop->items;
4090 for (i = lppop->nItems; i > 0; i--, item++)
4092 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4093 MENU_FreeItemData( item );
4095 HeapFree( GetProcessHeap(), 0, lppop->items );
4097 HeapFree( GetProcessHeap(), 0, lppop );
4102 /**********************************************************************
4103 * GetSystemMenu (USER32.@)
4105 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4107 WND *wndPtr = WIN_GetPtr( hWnd );
4110 if (wndPtr == WND_DESKTOP) return 0;
4111 if (wndPtr == WND_OTHER_PROCESS)
4113 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4117 if (wndPtr->hSysMenu && bRevert)
4119 DestroyMenu(wndPtr->hSysMenu);
4120 wndPtr->hSysMenu = 0;
4123 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4124 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4126 if( wndPtr->hSysMenu )
4129 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4131 /* Store the dummy sysmenu handle to facilitate the refresh */
4132 /* of the close button if the SC_CLOSE item change */
4133 menu = MENU_GetMenu(retvalue);
4135 menu->hSysMenuOwner = wndPtr->hSysMenu;
4137 WIN_ReleasePtr( wndPtr );
4139 return bRevert ? 0 : retvalue;
4143 /*******************************************************************
4144 * SetSystemMenu (USER32.@)
4146 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4148 WND *wndPtr = WIN_GetPtr( hwnd );
4150 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4152 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4153 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4154 WIN_ReleasePtr( wndPtr );
4161 /**********************************************************************
4162 * GetMenu (USER32.@)
4164 HMENU WINAPI GetMenu( HWND hWnd )
4166 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4167 TRACE("for %p returning %p\n", hWnd, retvalue);
4171 /**********************************************************************
4172 * GetMenuBarInfo (USER32.@)
4174 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4176 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4180 /**********************************************************************
4183 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4184 * SetWindowPos call that would result if SetMenu were called directly.
4186 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4188 TRACE("(%p, %p);\n", hWnd, hMenu);
4190 if (hMenu && !IsMenu(hMenu))
4192 WARN("hMenu %p is not a menu handle\n", hMenu);
4195 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4198 hWnd = WIN_GetFullHandle( hWnd );
4199 if (GetCapture() == hWnd)
4200 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4206 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4208 lpmenu->hWnd = hWnd;
4209 lpmenu->Height = 0; /* Make sure we recalculate the size */
4211 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4216 /**********************************************************************
4217 * SetMenu (USER32.@)
4219 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4221 if(!MENU_SetMenu(hWnd, hMenu))
4224 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4225 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4230 /**********************************************************************
4231 * GetSubMenu (USER32.@)
4233 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4237 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4238 if (!(lpmi->fType & MF_POPUP)) return 0;
4239 return lpmi->hSubMenu;
4243 /**********************************************************************
4244 * DrawMenuBar (USER32.@)
4246 BOOL WINAPI DrawMenuBar( HWND hWnd )
4249 HMENU hMenu = GetMenu(hWnd);
4251 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4253 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4255 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4256 lppop->hwndOwner = hWnd;
4257 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4258 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4262 /***********************************************************************
4263 * DrawMenuBarTemp (USER32.@)
4267 * called by W98SE desk.cpl Control Panel Applet
4269 * Not 100% sure about the param names, but close.
4271 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4276 BOOL flat_menu = FALSE;
4278 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4281 hMenu = GetMenu(hwnd);
4284 hFont = get_menu_font(FALSE);
4286 lppop = MENU_GetMenu( hMenu );
4287 if (lppop == NULL || lprect == NULL)
4289 retvalue = GetSystemMetrics(SM_CYMENU);
4293 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4295 hfontOld = SelectObject( hDC, hFont);
4297 if (lppop->Height == 0)
4298 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4300 lprect->bottom = lprect->top + lppop->Height;
4302 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4304 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4305 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4306 LineTo( hDC, lprect->right, lprect->bottom );
4308 if (lppop->nItems == 0)
4310 retvalue = GetSystemMetrics(SM_CYMENU);
4314 for (i = 0; i < lppop->nItems; i++)
4316 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4317 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4319 retvalue = lppop->Height;
4322 if (hfontOld) SelectObject (hDC, hfontOld);
4326 /***********************************************************************
4327 * EndMenu (USER.187)
4328 * EndMenu (USER32.@)
4330 BOOL WINAPI EndMenu(void)
4332 /* if we are in the menu code, and it is active */
4333 if (!fEndMenu && top_popup)
4335 /* terminate the menu handling code */
4338 /* needs to be posted to wakeup the internal menu handler */
4339 /* which will now terminate the menu, in the event that */
4340 /* the main window was minimized, or lost focus, so we */
4341 /* don't end up with an orphaned menu */
4342 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4348 /*****************************************************************
4349 * LoadMenuA (USER32.@)
4351 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4353 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4354 if (!hrsrc) return 0;
4355 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4359 /*****************************************************************
4360 * LoadMenuW (USER32.@)
4362 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4364 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4365 if (!hrsrc) return 0;
4366 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4370 /**********************************************************************
4371 * LoadMenuIndirectW (USER32.@)
4373 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4376 WORD version, offset;
4377 LPCSTR p = template;
4379 version = GET_WORD(p);
4381 TRACE("%p, ver %d\n", template, version );
4384 case 0: /* standard format is version of 0 */
4385 offset = GET_WORD(p);
4386 p += sizeof(WORD) + offset;
4387 if (!(hMenu = CreateMenu())) return 0;
4388 if (!MENU_ParseResource( p, hMenu ))
4390 DestroyMenu( hMenu );
4394 case 1: /* extended format is version of 1 */
4395 offset = GET_WORD(p);
4396 p += sizeof(WORD) + offset;
4397 if (!(hMenu = CreateMenu())) return 0;
4398 if (!MENUEX_ParseResource( p, hMenu))
4400 DestroyMenu( hMenu );
4405 ERR("version %d not supported.\n", version);
4411 /**********************************************************************
4412 * LoadMenuIndirectA (USER32.@)
4414 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4416 return LoadMenuIndirectW( template );
4420 /**********************************************************************
4423 BOOL WINAPI IsMenu(HMENU hmenu)
4425 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4429 SetLastError(ERROR_INVALID_MENU_HANDLE);
4435 /**********************************************************************
4436 * GetMenuItemInfo_common
4439 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4440 LPMENUITEMINFOW lpmii, BOOL unicode)
4442 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4444 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4447 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4451 if( lpmii->fMask & MIIM_TYPE) {
4452 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4453 WARN("invalid combination of fMask bits used\n");
4454 /* this does not happen on Win9x/ME */
4455 SetLastError( ERROR_INVALID_PARAMETER);
4458 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4459 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4460 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4461 if( lpmii->fType & MFT_BITMAP) {
4462 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4464 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4465 /* this does not happen on Win9x/ME */
4466 lpmii->dwTypeData = 0;
4471 /* copy the text string */
4472 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4474 if(lpmii->dwTypeData && lpmii->cch) {
4477 *((WCHAR *)lpmii->dwTypeData) = 0;
4479 *((CHAR *)lpmii->dwTypeData) = 0;
4485 len = strlenW(menu->text);
4486 if(lpmii->dwTypeData && lpmii->cch)
4487 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4491 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4492 0, NULL, NULL ) - 1;
4493 if(lpmii->dwTypeData && lpmii->cch)
4494 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4495 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4496 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4498 /* if we've copied a substring we return its length */
4499 if(lpmii->dwTypeData && lpmii->cch)
4500 if (lpmii->cch <= len + 1)
4505 /* return length of string */
4506 /* not on Win9x/ME if fType & MFT_BITMAP */
4512 if (lpmii->fMask & MIIM_FTYPE)
4513 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4515 if (lpmii->fMask & MIIM_BITMAP)
4516 lpmii->hbmpItem = menu->hbmpItem;
4518 if (lpmii->fMask & MIIM_STATE)
4519 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4521 if (lpmii->fMask & MIIM_ID)
4522 lpmii->wID = menu->wID;
4524 if (lpmii->fMask & MIIM_SUBMENU)
4525 lpmii->hSubMenu = menu->hSubMenu;
4527 /* hSubMenu is always cleared
4528 * (not on Win9x/ME ) */
4529 lpmii->hSubMenu = 0;
4532 if (lpmii->fMask & MIIM_CHECKMARKS) {
4533 lpmii->hbmpChecked = menu->hCheckBit;
4534 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4536 if (lpmii->fMask & MIIM_DATA)
4537 lpmii->dwItemData = menu->dwItemData;
4542 /**********************************************************************
4543 * GetMenuItemInfoA (USER32.@)
4545 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4546 LPMENUITEMINFOA lpmii)
4550 if( lpmii->cbSize != sizeof( mii) &&
4551 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4552 SetLastError( ERROR_INVALID_PARAMETER);
4555 memcpy( &mii, lpmii, lpmii->cbSize);
4556 mii.cbSize = sizeof( mii);
4557 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4558 (LPMENUITEMINFOW)&mii, FALSE);
4559 mii.cbSize = lpmii->cbSize;
4560 memcpy( lpmii, &mii, mii.cbSize);
4564 /**********************************************************************
4565 * GetMenuItemInfoW (USER32.@)
4567 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4568 LPMENUITEMINFOW lpmii)
4572 if( lpmii->cbSize != sizeof( mii) &&
4573 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4574 SetLastError( ERROR_INVALID_PARAMETER);
4577 memcpy( &mii, lpmii, lpmii->cbSize);
4578 mii.cbSize = sizeof( mii);
4579 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4580 mii.cbSize = lpmii->cbSize;
4581 memcpy( lpmii, &mii, mii.cbSize);
4586 /* set a menu item text from a ASCII or Unicode string */
4587 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4593 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4594 strcpyW( menu->text, text );
4598 LPCSTR str = (LPCSTR)text;
4599 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4600 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4601 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4606 /**********************************************************************
4609 * detect if there are loops in the menu tree (or the depth is too large)
4611 static int MENU_depth( POPUPMENU *pmenu, int depth)
4618 if( depth > MAXMENUDEPTH) return depth;
4619 item = pmenu->items;
4621 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4622 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4624 int bdepth = MENU_depth( psubmenu, depth);
4625 if( bdepth > subdepth) subdepth = bdepth;
4627 if( subdepth > MAXMENUDEPTH)
4628 TRACE("<- hmenu %p\n", item->hSubMenu);
4634 /**********************************************************************
4635 * SetMenuItemInfo_common
4637 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4638 * MIIM_BITMAP and MIIM_STRING flags instead.
4641 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4642 const MENUITEMINFOW *lpmii,
4645 if (!menu) return FALSE;
4647 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4649 if (lpmii->fMask & MIIM_FTYPE ) {
4650 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4651 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4653 if (lpmii->fMask & MIIM_STRING ) {
4654 /* free the string when used */
4655 HeapFree(GetProcessHeap(), 0, menu->text);
4656 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4659 if (lpmii->fMask & MIIM_STATE)
4660 /* Other menu items having MFS_DEFAULT are not converted
4662 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4664 if (lpmii->fMask & MIIM_ID)
4665 menu->wID = lpmii->wID;
4667 if (lpmii->fMask & MIIM_SUBMENU) {
4668 menu->hSubMenu = lpmii->hSubMenu;
4669 if (menu->hSubMenu) {
4670 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4672 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4673 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4677 subMenu->wFlags |= MF_POPUP;
4678 menu->fType |= MF_POPUP;
4680 SetLastError( ERROR_INVALID_PARAMETER);
4685 menu->fType &= ~MF_POPUP;
4688 if (lpmii->fMask & MIIM_CHECKMARKS)
4690 menu->hCheckBit = lpmii->hbmpChecked;
4691 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4693 if (lpmii->fMask & MIIM_DATA)
4694 menu->dwItemData = lpmii->dwItemData;
4696 if (lpmii->fMask & MIIM_BITMAP)
4697 menu->hbmpItem = lpmii->hbmpItem;
4699 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4700 menu->fType |= MFT_SEPARATOR;
4702 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4706 /**********************************************************************
4707 * MENU_NormalizeMenuItemInfoStruct
4709 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4710 * check, copy and extend the MENUITEMINFO struct from the version that the application
4711 * supplied to the version used by wine source. */
4712 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4713 MENUITEMINFOW *pmii_out )
4715 /* do we recognize the size? */
4716 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4717 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4718 SetLastError( ERROR_INVALID_PARAMETER);
4721 /* copy the fields that we have */
4722 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4723 /* if the hbmpItem member is missing then extend */
4724 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4725 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4726 pmii_out->hbmpItem = NULL;
4728 /* test for invalid bit combinations */
4729 if( (pmii_out->fMask & MIIM_TYPE &&
4730 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4731 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4732 WARN("invalid combination of fMask bits used\n");
4733 /* this does not happen on Win9x/ME */
4734 SetLastError( ERROR_INVALID_PARAMETER);
4737 /* convert old style (MIIM_TYPE) to the new */
4738 if( pmii_out->fMask & MIIM_TYPE){
4739 pmii_out->fMask |= MIIM_FTYPE;
4740 if( IS_STRING_ITEM(pmii_out->fType)){
4741 pmii_out->fMask |= MIIM_STRING;
4742 } else if( (pmii_out->fType) & MFT_BITMAP){
4743 pmii_out->fMask |= MIIM_BITMAP;
4744 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4750 /**********************************************************************
4751 * SetMenuItemInfoA (USER32.@)
4753 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4754 const MENUITEMINFOA *lpmii)
4758 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4760 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4762 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4766 /**********************************************************************
4767 * SetMenuItemInfoW (USER32.@)
4769 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4770 const MENUITEMINFOW *lpmii)
4774 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4776 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4777 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4778 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4781 /**********************************************************************
4782 * SetMenuDefaultItem (USER32.@)
4785 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4791 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4793 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4795 /* reset all default-item flags */
4797 for (i = 0; i < menu->nItems; i++, item++)
4799 item->fState &= ~MFS_DEFAULT;
4802 /* no default item */
4811 if ( uItem >= menu->nItems ) return FALSE;
4812 item[uItem].fState |= MFS_DEFAULT;
4817 for (i = 0; i < menu->nItems; i++, item++)
4819 if (item->wID == uItem)
4821 item->fState |= MFS_DEFAULT;
4830 /**********************************************************************
4831 * GetMenuDefaultItem (USER32.@)
4833 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4839 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4841 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4843 /* find default item */
4847 if (! item) return -1;
4849 while ( !( item->fState & MFS_DEFAULT ) )
4852 if (i >= menu->nItems ) return -1;
4855 /* default: don't return disabled items */
4856 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4858 /* search rekursiv when needed */
4859 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4862 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4863 if ( -1 != ret ) return ret;
4865 /* when item not found in submenu, return the popup item */
4867 return ( bypos ) ? i : item->wID;
4872 /**********************************************************************
4873 * InsertMenuItemA (USER32.@)
4875 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4876 const MENUITEMINFOA *lpmii)
4881 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4883 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4885 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4886 return SetMenuItemInfo_common(item, &mii, FALSE);
4890 /**********************************************************************
4891 * InsertMenuItemW (USER32.@)
4893 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4894 const MENUITEMINFOW *lpmii)
4899 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4901 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4903 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4904 return SetMenuItemInfo_common(item, &mii, TRUE);
4907 /**********************************************************************
4908 * CheckMenuRadioItem (USER32.@)
4911 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4912 UINT first, UINT last, UINT check,
4917 MENUITEM *mi_first = NULL, *mi_check;
4918 HMENU m_first, m_check;
4920 for (i = first; i <= last; i++)
4927 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4928 if (!mi_first) continue;
4929 mi_check = mi_first;
4935 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4936 if (!mi_check) continue;
4939 if (m_first != m_check) continue;
4940 if (mi_check->fType == MFT_SEPARATOR) continue;
4944 mi_check->fType |= MFT_RADIOCHECK;
4945 mi_check->fState |= MFS_CHECKED;
4950 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4951 mi_check->fState &= ~MFS_CHECKED;
4959 /**********************************************************************
4960 * GetMenuItemRect (USER32.@)
4962 * ATTENTION: Here, the returned values in rect are the screen
4963 * coordinates of the item just like if the menu was
4964 * always on the upper left side of the application.
4967 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4970 POPUPMENU *itemMenu;
4974 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4976 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4977 referenceHwnd = hwnd;
4981 itemMenu = MENU_GetMenu(hMenu);
4982 if (itemMenu == NULL)
4985 if(itemMenu->hWnd == 0)
4987 referenceHwnd = itemMenu->hWnd;
4990 if ((rect == NULL) || (item == NULL))
4995 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5000 /**********************************************************************
5001 * SetMenuInfo (USER32.@)
5004 * actually use the items to draw the menu
5005 * (recalculate and/or redraw)
5007 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5010 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5012 if (lpmi->fMask & MIM_BACKGROUND)
5013 menu->hbrBack = lpmi->hbrBack;
5015 if (lpmi->fMask & MIM_HELPID)
5016 menu->dwContextHelpID = lpmi->dwContextHelpID;
5018 if (lpmi->fMask & MIM_MAXHEIGHT)
5019 menu->cyMax = lpmi->cyMax;
5021 if (lpmi->fMask & MIM_MENUDATA)
5022 menu->dwMenuData = lpmi->dwMenuData;
5024 if (lpmi->fMask & MIM_STYLE)
5025 menu->dwStyle = lpmi->dwStyle;
5027 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5029 MENUITEM *item = menu->items;
5030 for( i = menu->nItems; i; i--, item++)
5031 if( item->fType & MF_POPUP)
5032 menu_SetMenuInfo( item->hSubMenu, lpmi);
5037 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5039 TRACE("(%p %p)\n", hMenu, lpmi);
5040 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5041 if( lpmi->fMask & MIM_STYLE) {
5042 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5043 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5044 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5048 SetLastError( ERROR_INVALID_PARAMETER);
5052 /**********************************************************************
5053 * GetMenuInfo (USER32.@)
5059 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5062 TRACE("(%p %p)\n", hMenu, lpmi);
5064 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5067 if (lpmi->fMask & MIM_BACKGROUND)
5068 lpmi->hbrBack = menu->hbrBack;
5070 if (lpmi->fMask & MIM_HELPID)
5071 lpmi->dwContextHelpID = menu->dwContextHelpID;
5073 if (lpmi->fMask & MIM_MAXHEIGHT)
5074 lpmi->cyMax = menu->cyMax;
5076 if (lpmi->fMask & MIM_MENUDATA)
5077 lpmi->dwMenuData = menu->dwMenuData;
5079 if (lpmi->fMask & MIM_STYLE)
5080 lpmi->dwStyle = menu->dwStyle;
5084 SetLastError( ERROR_INVALID_PARAMETER);
5089 /**********************************************************************
5090 * SetMenuContextHelpId (USER32.@)
5092 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5096 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5098 if ((menu = MENU_GetMenu(hMenu)))
5100 menu->dwContextHelpID = dwContextHelpID;
5107 /**********************************************************************
5108 * GetMenuContextHelpId (USER32.@)
5110 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5114 TRACE("(%p)\n", hMenu);
5116 if ((menu = MENU_GetMenu(hMenu)))
5118 return menu->dwContextHelpID;
5123 /**********************************************************************
5124 * MenuItemFromPoint (USER32.@)
5126 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5128 POPUPMENU *menu = MENU_GetMenu(hMenu);
5131 /*FIXME: Do we have to handle hWnd here? */
5132 if (!menu) return -1;
5133 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5138 /**********************************************************************
5139 * translate_accelerator
5141 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5142 BYTE fVirt, WORD key, WORD cmd )
5147 if (wParam != key) return FALSE;
5149 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5150 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5151 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5153 if (message == WM_CHAR || message == WM_SYSCHAR)
5155 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5157 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5163 if(fVirt & FVIRTKEY)
5165 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5166 wParam, 0xff & HIWORD(lParam));
5168 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5169 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5173 if (!(lParam & 0x01000000)) /* no special_key */
5175 if ((fVirt & FALT) && (lParam & 0x20000000))
5176 { /* ^^ ALT pressed */
5177 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5186 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5190 HMENU hMenu, hSubMenu, hSysMenu;
5191 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5193 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5194 hSysMenu = get_win_sys_menu( hWnd );
5196 /* find menu item and ask application to initialize it */
5197 /* 1. in the system menu */
5198 hSubMenu = hSysMenu;
5200 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5204 if (!IsWindowEnabled(hWnd))
5208 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5209 if(hSubMenu != hSysMenu)
5211 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5212 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5213 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5215 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5218 else /* 2. in the window's menu */
5222 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5226 if (!IsWindowEnabled(hWnd))
5230 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5231 if(hSubMenu != hMenu)
5233 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5234 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5235 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5237 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5244 if (uSysStat != (UINT)-1)
5246 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5253 if (uStat != (UINT)-1)
5259 if (uStat & (MF_DISABLED|MF_GRAYED))
5271 if( mesg==WM_COMMAND )
5273 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5274 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5276 else if( mesg==WM_SYSCOMMAND )
5278 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5279 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5283 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5284 * #0: unknown (please report!)
5285 * #1: for WM_KEYUP,WM_SYSKEYUP
5286 * #2: mouse is captured
5287 * #3: window is disabled
5288 * #4: it's a disabled system menu option
5289 * #5: it's a menu option, but window is iconic
5290 * #6: it's a menu option, but disabled
5292 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5294 ERR_(accel)(" unknown reason - please report!\n");
5299 /**********************************************************************
5300 * TranslateAcceleratorA (USER32.@)
5301 * TranslateAccelerator (USER32.@)
5303 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5305 switch (msg->message)
5309 return TranslateAcceleratorW( hWnd, hAccel, msg );
5315 char ch = LOWORD(msg->wParam);
5317 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5318 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5319 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5327 /**********************************************************************
5328 * TranslateAcceleratorW (USER32.@)
5330 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5332 ACCEL data[32], *ptr = data;
5335 if (!hWnd) return 0;
5337 if (msg->message != WM_KEYDOWN &&
5338 msg->message != WM_SYSKEYDOWN &&
5339 msg->message != WM_CHAR &&
5340 msg->message != WM_SYSCHAR)
5343 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5344 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5346 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5347 if (count > sizeof(data)/sizeof(data[0]))
5349 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5351 count = CopyAcceleratorTableW( hAccel, ptr, count );
5352 for (i = 0; i < count; i++)
5354 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5355 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5358 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );