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