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 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
188 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
190 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
192 /*********************************************************************
193 * menu class descriptor
195 const struct builtin_class_descr MENU_builtin_class =
197 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
198 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
199 NULL, /* procA (winproc is Unicode only) */
200 PopupMenuWndProc, /* procW */
201 sizeof(HMENU), /* extra */
202 IDC_ARROW, /* cursor */
203 (HBRUSH)(COLOR_MENU+1) /* brush */
207 /***********************************************************************
208 * debug_print_menuitem
210 * Print a menuitem in readable form.
213 #define debug_print_menuitem(pre, mp, post) \
214 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
216 #define MENUOUT(text) \
217 TRACE("%s%s", (count++ ? "," : ""), (text))
219 #define MENUFLAG(bit,text) \
221 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
224 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
227 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
228 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
229 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
230 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
231 TRACE("%s ", prefix);
233 UINT flags = mp->fType;
234 TRACE( "{ ID=0x%lx", mp->wID);
236 TRACE( ", Sub=%p", mp->hSubMenu);
240 MENUFLAG( MFT_SEPARATOR, "sep");
241 MENUFLAG( MFT_OWNERDRAW, "own");
242 MENUFLAG( MFT_BITMAP, "bit");
243 MENUFLAG(MF_POPUP, "pop");
244 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
245 MENUFLAG(MFT_MENUBREAK, "brk");
246 MENUFLAG(MFT_RADIOCHECK, "radio");
247 MENUFLAG(MFT_RIGHTORDER, "rorder");
248 MENUFLAG(MF_SYSMENU, "sys");
249 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
251 TRACE( "+0x%x", flags);
257 MENUFLAG(MFS_GRAYED, "grey");
258 MENUFLAG(MFS_DEFAULT, "default");
259 MENUFLAG(MFS_DISABLED, "dis");
260 MENUFLAG(MFS_CHECKED, "check");
261 MENUFLAG(MFS_HILITE, "hi");
262 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
263 MENUFLAG(MF_MOUSESELECT, "mouse");
265 TRACE( "+0x%x", flags);
268 TRACE( ", Chk=%p", mp->hCheckBit);
270 TRACE( ", Unc=%p", mp->hUnCheckBit);
272 TRACE( ", Text=%s", debugstr_w(mp->text));
274 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
277 if( IS_MAGIC_BITMAP(mp->hbmpItem))
278 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
280 TRACE( ", hbitmap=%p", mp->hbmpItem);
285 TRACE(" %s\n", postfix);
292 /***********************************************************************
295 * Validate the given menu handle and returns the menu structure pointer.
297 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
299 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
301 if (menu == OBJ_OTHER_PROCESS)
303 WARN( "other process menu %p?\n", hMenu);
306 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
307 else WARN("invalid menu handle=%p\n", hMenu);
311 /***********************************************************************
314 * Get the system menu of a window
316 static HMENU get_win_sys_menu( HWND hwnd )
319 WND *win = WIN_GetPtr( hwnd );
320 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
323 WIN_ReleasePtr( win );
328 /***********************************************************************
331 static HFONT get_menu_font( BOOL bold )
333 static HFONT hMenuFont, hMenuFontBold;
335 HFONT ret = bold ? hMenuFontBold : hMenuFont;
339 NONCLIENTMETRICSW ncm;
342 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
343 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
347 ncm.lfMenuFont.lfWeight += 300;
348 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
350 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
351 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
355 /* another thread beat us to it */
363 /***********************************************************************
366 static HBITMAP get_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
374 /***********************************************************************
375 * get_down_arrow_bitmap
377 static HBITMAP get_down_arrow_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
385 /***********************************************************************
386 * get_down_arrow_inactive_bitmap
388 static HBITMAP get_down_arrow_inactive_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
396 /***********************************************************************
397 * get_up_arrow_bitmap
399 static HBITMAP get_up_arrow_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
407 /***********************************************************************
408 * get_up_arrow_inactive_bitmap
410 static HBITMAP get_up_arrow_inactive_bitmap(void)
412 static HBITMAP arrow_bitmap;
414 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
418 /***********************************************************************
421 * Return the default system menu.
423 static HMENU MENU_CopySysPopup(void)
425 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
426 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
430 MENUITEMINFOW miteminfo;
431 POPUPMENU* menu = MENU_GetMenu(hMenu);
432 menu->wFlags |= MF_SYSMENU | MF_POPUP;
433 /* decorate the menu with bitmaps */
434 minfo.cbSize = sizeof( MENUINFO);
435 minfo.dwStyle = MNS_CHECKORBMP;
436 minfo.fMask = MIM_STYLE;
437 SetMenuInfo( hMenu, &minfo);
438 miteminfo.cbSize = sizeof( MENUITEMINFOW);
439 miteminfo.fMask = MIIM_BITMAP;
440 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
441 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
443 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
446 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
447 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
448 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
451 ERR("Unable to load default system menu\n" );
453 TRACE("returning %p.\n", hMenu );
459 /**********************************************************************
462 * Create a copy of the system menu. System menu in Windows is
463 * a special menu bar with the single entry - system menu popup.
464 * This popup is presented to the outside world as a "system menu".
465 * However, the real system menu handle is sometimes seen in the
466 * WM_MENUSELECT parameters (and Word 6 likes it this way).
468 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
472 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
473 if ((hMenu = CreateMenu()))
475 POPUPMENU *menu = MENU_GetMenu(hMenu);
476 menu->wFlags = MF_SYSMENU;
477 menu->hWnd = WIN_GetFullHandle( hWnd );
478 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
481 hPopupMenu = MENU_CopySysPopup();
485 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
486 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
488 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
489 (UINT_PTR)hPopupMenu, NULL );
491 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
492 menu->items[0].fState = 0;
493 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
495 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
498 DestroyMenu( hMenu );
500 ERR("failed to load system menu!\n");
505 /***********************************************************************
506 * MENU_InitSysMenuPopup
508 * Grey the appropriate items in System menu.
510 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
514 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
515 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = ((style & WS_MAXIMIZE) != 0);
517 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
518 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
519 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
520 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
521 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
522 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
523 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
524 gray = (clsStyle & CS_NOCLOSE) != 0;
526 /* The menu item must keep its state if it's disabled */
528 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
532 /******************************************************************************
534 * UINT MENU_GetStartOfNextColumn(
537 *****************************************************************************/
539 static UINT MENU_GetStartOfNextColumn(
542 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 return NO_SELECTED_ITEM;
548 i = menu->FocusedItem + 1;
549 if( i == NO_SELECTED_ITEM )
552 for( ; i < menu->nItems; ++i ) {
553 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return NO_SELECTED_ITEM;
561 /******************************************************************************
563 * UINT MENU_GetStartOfPrevColumn(
566 *****************************************************************************/
568 static UINT MENU_GetStartOfPrevColumn(
571 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 return NO_SELECTED_ITEM;
577 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
578 return NO_SELECTED_ITEM;
580 /* Find the start of the column */
582 for(i = menu->FocusedItem; i != 0 &&
583 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 return NO_SELECTED_ITEM;
589 for(--i; i != 0; --i) {
590 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 TRACE("ret %d.\n", i );
601 /***********************************************************************
604 * Find a menu item. Return a pointer on the item, and modifies *hmenu
605 * in case the item was in a sub-menu.
607 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
610 MENUITEM *fallback = NULL;
611 UINT fallback_pos = 0;
614 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
615 if (wFlags & MF_BYPOSITION)
617 if (*nPos >= menu->nItems) return NULL;
618 return &menu->items[*nPos];
622 MENUITEM *item = menu->items;
623 for (i = 0; i < menu->nItems; i++, item++)
625 if (item->fType & MF_POPUP)
627 HMENU hsubmenu = item->hSubMenu;
628 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
634 else if (item->wID == *nPos)
636 /* fallback to this item if nothing else found */
641 else if (item->wID == *nPos)
650 *nPos = fallback_pos;
655 /***********************************************************************
658 * Find a Sub menu. Return the position of the submenu, and modifies
659 * *hmenu in case it is found in another sub-menu.
660 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
662 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
667 if (((*hmenu)==(HMENU)0xffff) ||
668 (!(menu = MENU_GetMenu(*hmenu))))
669 return NO_SELECTED_ITEM;
671 for (i = 0; i < menu->nItems; i++, item++) {
672 if(!(item->fType & MF_POPUP)) continue;
673 if (item->hSubMenu == hSubTarget) {
677 HMENU hsubmenu = item->hSubMenu;
678 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
679 if (pos != NO_SELECTED_ITEM) {
685 return NO_SELECTED_ITEM;
688 /***********************************************************************
691 static void MENU_FreeItemData( MENUITEM* item )
694 HeapFree( GetProcessHeap(), 0, item->text );
697 /***********************************************************************
698 * MENU_AdjustMenuItemRect
700 * Adjust menu item rectangle according to scrolling state.
703 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
705 if (menu->bScrolling)
707 UINT arrow_bitmap_height;
710 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
711 arrow_bitmap_height = bmp.bmHeight;
712 rect->top += arrow_bitmap_height - menu->nScrollPos;
713 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
718 /***********************************************************************
719 * MENU_FindItemByCoords
721 * Find the item at the specified coordinates (screen coords). Does
722 * not work for child windows and therefore should not be called for
723 * an arbitrary system menu.
725 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
726 POINT pt, UINT *pos )
732 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 for (i = 0; i < menu->nItems; i++, item++)
739 MENU_AdjustMenuItemRect(menu, &rect);
740 if (PtInRect(&rect, pt))
750 /***********************************************************************
753 * Find the menu item selected by a key press.
754 * Return item id, -1 if none, -2 if we should close the menu.
756 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
757 WCHAR key, BOOL forceMenuChar )
759 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
761 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
765 POPUPMENU *menu = MENU_GetMenu( hmenu );
766 MENUITEM *item = menu->items;
773 for (i = 0; i < menu->nItems; i++, item++)
777 WCHAR *p = item->text - 2;
780 p = strchrW (p + 2, '&');
782 while (p != NULL && p [1] == '&');
783 if (p && (toupperW(p[1]) == toupperW(key))) return i;
787 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
788 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
789 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
790 if (HIWORD(menuchar) == 1) return (UINT)(-2);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
805 HBITMAP bmp = lpitem->hbmpItem;
807 size->cx = size->cy = 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR) bmp )
812 case (INT_PTR)HBMMENU_CALLBACK:
814 MEASUREITEMSTRUCT measItem;
815 measItem.CtlType = ODT_MENU;
817 measItem.itemID = lpitem->wID;
818 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
819 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
820 measItem.itemData = lpitem->dwItemData;
821 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
822 size->cx = measItem.itemWidth;
823 size->cy = measItem.itemHeight;
827 case (INT_PTR)HBMMENU_SYSTEM:
828 if (lpitem->dwItemData)
830 bmp = (HBITMAP)lpitem->dwItemData;
834 case (INT_PTR)HBMMENU_MBAR_RESTORE:
835 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
836 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
837 case (INT_PTR)HBMMENU_MBAR_CLOSE:
838 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
839 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
842 case (INT_PTR)HBMMENU_POPUP_CLOSE:
843 case (INT_PTR)HBMMENU_POPUP_RESTORE:
844 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
845 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
846 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
847 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
850 if (GetObjectW(bmp, sizeof(bm), &bm ))
852 size->cx = bm.bmWidth;
853 size->cy = bm.bmHeight;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
863 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
869 int w = rect->right - rect->left;
870 int h = rect->bottom - rect->top;
873 HBITMAP hbmToDraw = lpitem->hbmpItem;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw))
883 switch((INT_PTR)hbmToDraw)
885 case (INT_PTR)HBMMENU_SYSTEM:
886 if (lpitem->dwItemData)
888 bmp = (HBITMAP)lpitem->dwItemData;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
893 static HBITMAP hBmpSysMenu;
895 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
897 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset = bm.bmWidth / 2;
900 bm.bmWidth -= bmp_xoffset;
903 case (INT_PTR)HBMMENU_MBAR_RESTORE:
904 flags = DFCS_CAPTIONRESTORE;
906 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
907 flags = DFCS_CAPTIONMIN;
909 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
910 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
912 case (INT_PTR)HBMMENU_MBAR_CLOSE:
913 flags = DFCS_CAPTIONCLOSE;
915 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
916 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
918 case (INT_PTR)HBMMENU_CALLBACK:
920 DRAWITEMSTRUCT drawItem;
921 drawItem.CtlType = ODT_MENU;
923 drawItem.itemID = lpitem->wID;
924 drawItem.itemAction = odaction;
925 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
926 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
927 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
928 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
929 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
930 drawItem.hwndItem = (HWND)hmenu;
932 drawItem.itemData = lpitem->dwItemData;
933 drawItem.rcItem = *rect;
934 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
938 case (INT_PTR)HBMMENU_POPUP_CLOSE:
941 case (INT_PTR)HBMMENU_POPUP_RESTORE:
944 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
947 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
951 FIXME("Magic %p not implemented\n", hbmToDraw);
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont, hfontsav;
959 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
960 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont.lfHeight = min( h, w) - 5 ;
963 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
964 hfont = CreateFontIndirectW( &logfont);
965 hfontsav = SelectObject(hdc, hfont);
966 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
967 SelectObject(hdc, hfontsav);
968 DeleteObject( hfont);
973 InflateRect( &r, -1, -1 );
974 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
975 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
980 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
983 hdcMem = CreateCompatibleDC( hdc );
984 SelectObject( hdcMem, bmp );
986 /* handle fontsize > bitmap_height */
987 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
989 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
990 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
991 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
992 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
997 /***********************************************************************
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1003 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1006 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1007 UINT arrow_bitmap_width;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1013 (menuBar ? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1016 arrow_bitmap_width = bm.bmWidth;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize.cx ) {
1020 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight = HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1028 if (lpitem->fType & MF_OWNERDRAW)
1030 MEASUREITEMSTRUCT mis;
1031 mis.CtlType = ODT_MENU;
1033 mis.itemID = lpitem->wID;
1034 mis.itemData = lpitem->dwItemData;
1035 mis.itemHeight = ODitemheight;
1037 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1038 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1047 lpitem->rect.bottom += mis.itemHeight;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1051 lpitem->rect.bottom-lpitem->rect.top);
1055 if (lpitem->fType & MF_SEPARATOR)
1057 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1059 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1067 if (lpitem->hbmpItem) {
1070 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem->bmpsize = size;
1074 lppop->textOffset = max( lppop->textOffset, size.cx);
1075 lpitem->rect.right += size.cx + 2;
1076 itemheight = size.cy + 2;
1078 if( !(lppop->dwStyle & MNS_NOCHECK))
1079 lpitem->rect.right += check_bitmap_width;
1080 lpitem->rect.right += 4 + menucharsize.cx;
1081 lpitem->xTab = lpitem->rect.right;
1082 lpitem->rect.right += arrow_bitmap_width;
1083 } else if (lpitem->hbmpItem) { /* menuBar */
1086 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1087 lpitem->bmpsize = size;
1088 lpitem->rect.right += size.cx;
1089 if( lpitem->text) lpitem->rect.right += 2;
1090 itemheight = size.cy;
1093 /* it must be a text item - unless it's the system menu */
1094 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1095 HFONT hfontOld = NULL;
1096 RECT rc = lpitem->rect;
1097 LONG txtheight, txtwidth;
1099 if ( lpitem->fState & MFS_DEFAULT ) {
1100 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1103 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1104 DT_SINGLELINE|DT_CALCRECT);
1105 lpitem->rect.right += rc.right - rc.left;
1106 itemheight = max( max( itemheight, txtheight),
1107 GetSystemMetrics( SM_CYMENU) - 1);
1108 lpitem->rect.right += 2 * menucharsize.cx;
1110 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1113 int n = (int)( p - lpitem->text);
1114 /* Item contains a tab (only meaningful in popup menus) */
1115 /* get text size before the tab */
1116 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1117 DT_SINGLELINE|DT_CALCRECT);
1118 txtwidth = rc.right - rc.left;
1119 p += 1; /* advance past the Tab */
1120 /* get text size after the tab */
1121 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1122 DT_SINGLELINE|DT_CALCRECT);
1123 lpitem->xTab += txtwidth;
1124 txtheight = max( txtheight, tmpheight);
1125 txtwidth += menucharsize.cx + /* space for the tab */
1126 tmprc.right - tmprc.left; /* space for the short cut */
1128 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 txtwidth = rc.right - rc.left;
1131 lpitem->xTab += txtwidth;
1133 lpitem->rect.right += 2 + txtwidth;
1134 itemheight = max( itemheight,
1135 max( txtheight + 2, menucharsize.cy + 4));
1137 if (hfontOld) SelectObject (hdc, hfontOld);
1138 } else if( menuBar) {
1139 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1141 lpitem->rect.bottom += itemheight;
1142 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1146 /***********************************************************************
1147 * MENU_GetMaxPopupHeight
1150 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1153 return lppop->cyMax;
1154 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1158 /***********************************************************************
1159 * MENU_PopupMenuCalcSize
1161 * Calculate the size of a popup menu.
1163 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1168 int textandbmp = FALSE;
1169 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1171 lppop->Width = lppop->Height = 0;
1172 if (lppop->nItems == 0) return;
1175 SelectObject( hdc, get_menu_font(FALSE));
1180 lppop->textOffset = 0;
1182 while (start < lppop->nItems)
1184 lpitem = &lppop->items[start];
1186 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1187 orgX += MENU_COL_SPACE;
1188 orgY = MENU_TOP_MARGIN;
1190 maxTab = maxTabWidth = 0;
1191 /* Parse items until column break or end of menu */
1192 for (i = start; i < lppop->nItems; i++, lpitem++)
1195 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1197 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1198 maxX = max( maxX, lpitem->rect.right );
1199 orgY = lpitem->rect.bottom;
1200 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1202 maxTab = max( maxTab, lpitem->xTab );
1203 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1205 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX = max( maxX, maxTab + maxTabWidth );
1210 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1212 lpitem->rect.right = maxX;
1213 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1214 lpitem->xTab = maxTab;
1217 lppop->Height = max( lppop->Height, orgY );
1220 lppop->Width = maxX;
1221 /* if none of the items have both text and bitmap then
1222 * the text and bitmaps are all aligned on the left. If there is at
1223 * least one item with both text and bitmap then bitmaps are
1224 * on the left and texts left aligned with the right hand side
1226 if( !textandbmp) lppop->textOffset = 0;
1228 /* space for 3d border */
1229 lppop->Height += MENU_BOTTOM_MARGIN;
1232 /* Adjust popup height if it exceeds maximum */
1233 maxHeight = MENU_GetMaxPopupHeight(lppop);
1234 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1235 if (lppop->Height >= maxHeight)
1237 lppop->Height = maxHeight;
1238 lppop->bScrolling = TRUE;
1242 lppop->bScrolling = FALSE;
1245 ReleaseDC( 0, hdc );
1249 /***********************************************************************
1250 * MENU_MenuBarCalcSize
1252 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1253 * height is off by 1 pixel which causes lengthy window relocations when
1254 * active document window is maximized/restored.
1256 * Calculate the size of the menu bar.
1258 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1259 LPPOPUPMENU lppop, HWND hwndOwner )
1262 UINT start, i, helpPos;
1263 int orgX, orgY, maxY;
1265 if ((lprect == NULL) || (lppop == NULL)) return;
1266 if (lppop->nItems == 0) return;
1267 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1268 lppop->Width = lprect->right - lprect->left;
1270 maxY = lprect->top+1;
1273 lppop->textOffset = 0;
1274 while (start < lppop->nItems)
1276 lpitem = &lppop->items[start];
1277 orgX = lprect->left;
1280 /* Parse items until line break or end of menu */
1281 for (i = start; i < lppop->nItems; i++, lpitem++)
1283 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1285 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1287 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1288 debug_print_menuitem (" item: ", lpitem, "");
1289 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1291 if (lpitem->rect.right > lprect->right)
1293 if (i != start) break;
1294 else lpitem->rect.right = lprect->right;
1296 maxY = max( maxY, lpitem->rect.bottom );
1297 orgX = lpitem->rect.right;
1300 /* Finish the line (set all items to the largest height found) */
1301 while (start < i) lppop->items[start++].rect.bottom = maxY;
1304 lprect->bottom = maxY;
1305 lppop->Height = lprect->bottom - lprect->top;
1307 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1308 /* the last item (if several lines, only move the last line) */
1309 if (helpPos == ~0U) return;
1310 lpitem = &lppop->items[lppop->nItems-1];
1311 orgY = lpitem->rect.top;
1312 orgX = lprect->right;
1313 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1314 if (lpitem->rect.top != orgY) break; /* Other line */
1315 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1316 lpitem->rect.left += orgX - lpitem->rect.right;
1317 lpitem->rect.right = orgX;
1318 orgX = lpitem->rect.left;
1323 /***********************************************************************
1324 * MENU_DrawScrollArrows
1326 * Draw scroll arrows.
1329 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1331 HDC hdcMem = CreateCompatibleDC(hdc);
1332 HBITMAP hOrigBitmap;
1333 UINT arrow_bitmap_width, arrow_bitmap_height;
1337 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1338 arrow_bitmap_width = bmp.bmWidth;
1339 arrow_bitmap_height = bmp.bmHeight;
1342 if (lppop->nScrollPos)
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1345 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1348 rect.right = lppop->Width;
1349 rect.bottom = arrow_bitmap_height;
1350 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1351 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1352 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1353 rect.top = lppop->Height - arrow_bitmap_height;
1354 rect.bottom = lppop->Height;
1355 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1356 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1357 SelectObject(hdcMem, get_down_arrow_bitmap());
1359 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1360 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1361 lppop->Height - arrow_bitmap_height,
1362 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1363 SelectObject(hdcMem, hOrigBitmap);
1368 /***********************************************************************
1371 * Draws the popup-menu arrow.
1373 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1374 UINT arrow_bitmap_height)
1376 HDC hdcMem = CreateCompatibleDC( hdc );
1377 HBITMAP hOrigBitmap;
1379 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1380 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1381 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1382 arrow_bitmap_width, arrow_bitmap_height,
1383 hdcMem, 0, 0, SRCCOPY );
1384 SelectObject( hdcMem, hOrigBitmap );
1387 /***********************************************************************
1390 * Draw a single menu item.
1392 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1393 UINT height, BOOL menuBar, UINT odaction )
1396 BOOL flat_menu = FALSE;
1398 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1399 POPUPMENU *menu = MENU_GetMenu(hmenu);
1402 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1406 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1407 arrow_bitmap_width = bmp.bmWidth;
1408 arrow_bitmap_height = bmp.bmHeight;
1411 if (lpitem->fType & MF_SYSMENU)
1413 if( !IsIconic(hwnd) )
1414 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1418 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1419 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1423 if (lpitem->fState & MF_HILITE)
1425 if(menuBar && !flat_menu) {
1426 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1427 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1429 if(lpitem->fState & MF_GRAYED)
1430 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1432 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1433 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1438 if (lpitem->fState & MF_GRAYED)
1439 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1441 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1442 SetBkColor( hdc, GetSysColor( bkgnd ) );
1445 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1446 rect = lpitem->rect;
1447 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1449 if (lpitem->fType & MF_OWNERDRAW)
1452 ** Experimentation under Windows reveals that an owner-drawn
1453 ** menu is given the rectangle which includes the space it requested
1454 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1455 ** and a popup-menu arrow. This is the value of lpitem->rect.
1456 ** Windows will leave all drawing to the application except for
1457 ** the popup-menu arrow. Windows always draws that itself, after
1458 ** the menu owner has finished drawing.
1462 dis.CtlType = ODT_MENU;
1464 dis.itemID = lpitem->wID;
1465 dis.itemData = lpitem->dwItemData;
1467 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1468 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1469 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1470 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1471 dis.hwndItem = (HWND)hmenu;
1474 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1475 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1476 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1477 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1478 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1479 /* Draw the popup-menu arrow */
1480 if (lpitem->fType & MF_POPUP)
1481 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1482 arrow_bitmap_height);
1486 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1488 if (lpitem->fState & MF_HILITE)
1492 InflateRect (&rect, -1, -1);
1493 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1494 InflateRect (&rect, 1, 1);
1495 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1500 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1502 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1506 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1508 SetBkMode( hdc, TRANSPARENT );
1510 /* vertical separator */
1511 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1516 rc.left -= MENU_COL_SPACE / 2 + 1;
1518 rc.bottom = height - 3;
1521 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1522 MoveToEx( hdc, rc.left, rc.top, NULL );
1523 LineTo( hdc, rc.left, rc.bottom );
1524 SelectObject( hdc, oldPen );
1527 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1530 /* horizontal separator */
1531 if (lpitem->fType & MF_SEPARATOR)
1538 rc.top = ( rc.top + rc.bottom) / 2;
1541 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1542 MoveToEx( hdc, rc.left, rc.top, NULL );
1543 LineTo( hdc, rc.right, rc.top );
1544 SelectObject( hdc, oldPen );
1547 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1551 /* helper lines for debugging */
1552 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1553 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1554 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1555 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1558 if (lpitem->hbmpItem) {
1559 /* calculate the bitmap rectangle in coordinates relative
1560 * to the item rectangle */
1562 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1565 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1567 else if (menu->dwStyle & MNS_NOCHECK)
1569 else if (menu->dwStyle & MNS_CHECKORBMP)
1572 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1573 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1574 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1577 bmprc.top = (rect.bottom - rect.top -
1578 lpitem->bmpsize.cy) / 2;
1579 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1585 INT y = rect.top + rect.bottom;
1587 int checked = FALSE;
1588 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1589 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1590 /* Draw the check mark
1593 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1595 if( !(menu->dwStyle & MNS_NOCHECK)) {
1596 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1597 lpitem->hUnCheckBit;
1598 if (bm) /* we have a custom bitmap */
1600 HDC hdcMem = CreateCompatibleDC( hdc );
1602 SelectObject( hdcMem, bm );
1603 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1604 check_bitmap_width, check_bitmap_height,
1605 hdcMem, 0, 0, SRCCOPY );
1609 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1612 HBITMAP bm = CreateBitmap( check_bitmap_width,
1613 check_bitmap_height, 1, 1, NULL );
1614 HDC hdcMem = CreateCompatibleDC( hdc );
1616 SelectObject( hdcMem, bm );
1617 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1618 DrawFrameControl( hdcMem, &r, DFC_MENU,
1619 (lpitem->fType & MFT_RADIOCHECK) ?
1620 DFCS_MENUBULLET : DFCS_MENUCHECK );
1621 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1622 hdcMem, 0, 0, SRCCOPY );
1628 if( lpitem->hbmpItem &&
1629 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1631 /* some applications make this assumption on the DC's origin */
1632 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1633 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1635 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1637 /* Draw the popup-menu arrow */
1638 if (lpitem->fType & MF_POPUP)
1639 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1640 arrow_bitmap_height);
1642 if( !(menu->dwStyle & MNS_NOCHECK))
1643 rect.left += check_bitmap_width;
1644 rect.right -= arrow_bitmap_width;
1646 else if( lpitem->hbmpItem)
1647 { /* Draw the bitmap */
1650 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1651 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1653 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1655 /* process text if present */
1661 UINT uFormat = (menuBar) ?
1662 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1663 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1665 if( !(menu->dwStyle & MNS_CHECKORBMP))
1666 rect.left += menu->textOffset;
1668 if ( lpitem->fState & MFS_DEFAULT )
1670 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1674 if( lpitem->hbmpItem)
1675 rect.left += lpitem->bmpsize.cx;
1676 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1677 rect.left += menucharsize.cx;
1678 rect.right -= menucharsize.cx;
1681 for (i = 0; lpitem->text[i]; i++)
1682 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1685 if(lpitem->fState & MF_GRAYED)
1687 if (!(lpitem->fState & MF_HILITE) )
1689 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1690 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1691 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1692 --rect.left; --rect.top; --rect.right; --rect.bottom;
1694 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1697 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1699 /* paint the shortcut text */
1700 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1702 if (lpitem->text[i] == '\t')
1704 rect.left = lpitem->xTab;
1705 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1709 rect.right = lpitem->xTab;
1710 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1713 if(lpitem->fState & MF_GRAYED)
1715 if (!(lpitem->fState & MF_HILITE) )
1717 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1718 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1719 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1720 --rect.left; --rect.top; --rect.right; --rect.bottom;
1722 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1724 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1728 SelectObject (hdc, hfontOld);
1733 /***********************************************************************
1734 * MENU_DrawPopupMenu
1736 * Paint a popup menu.
1738 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1740 HBRUSH hPrevBrush = 0;
1743 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1745 GetClientRect( hwnd, &rect );
1747 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1748 && (SelectObject( hdc, get_menu_font(FALSE))))
1752 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1754 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1758 BOOL flat_menu = FALSE;
1760 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1762 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1764 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1766 if( (menu = MENU_GetMenu( hmenu )))
1768 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1769 /* draw menu items */
1776 for( u = menu->nItems; u > 0; u--, item++)
1777 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1778 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1780 /* draw scroll arrows */
1781 if (menu->bScrolling)
1782 MENU_DrawScrollArrows(menu, hdc);
1786 SelectObject( hdc, hPrevBrush );
1791 /***********************************************************************
1794 * Paint a menu bar. Returns the height of the menu bar.
1795 * called from [windows/nonclient.c]
1797 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1802 HMENU hMenu = GetMenu(hwnd);
1804 lppop = MENU_GetMenu( hMenu );
1805 if (lppop == NULL || lprect == NULL)
1807 return GetSystemMetrics(SM_CYMENU);
1812 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1814 if (lppop->Height == 0)
1815 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1817 lprect->bottom = lprect->top + lppop->Height;
1819 if (hfontOld) SelectObject( hDC, hfontOld);
1820 return lppop->Height;
1823 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1827 /***********************************************************************
1830 * Display a popup menu.
1832 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1833 INT x, INT y, INT xanchor, INT yanchor )
1841 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1842 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1844 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1845 if (menu->FocusedItem != NO_SELECTED_ITEM)
1847 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1848 menu->FocusedItem = NO_SELECTED_ITEM;
1851 /* store the owner for DrawItem */
1852 menu->hwndOwner = hwndOwner;
1854 menu->nScrollPos = 0;
1855 MENU_PopupMenuCalcSize( menu );
1857 /* adjust popup menu pos so that it fits within the desktop */
1859 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1860 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1862 /* FIXME: should use item rect */
1865 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1866 info.cbSize = sizeof(info);
1867 GetMonitorInfoW( monitor, &info );
1869 if( flags & TPM_RIGHTALIGN ) x -= width;
1870 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1872 if( flags & TPM_BOTTOMALIGN ) y -= height;
1873 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1875 if( x + width > info.rcWork.right)
1877 if( xanchor && x >= width - xanchor )
1878 x -= width - xanchor;
1880 if( x + width > info.rcWork.right)
1881 x = info.rcWork.right - width;
1883 if( x < info.rcWork.left ) x = info.rcWork.left;
1885 if( y + height > info.rcWork.bottom)
1887 if( yanchor && y >= height + yanchor )
1888 y -= height + yanchor;
1890 if( y + height > info.rcWork.bottom)
1891 y = info.rcWork.bottom - height;
1893 if( y < info.rcWork.top ) y = info.rcWork.top;
1895 /* NOTE: In Windows, top menu popup is not owned. */
1896 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1897 WS_POPUP, x, y, width, height,
1898 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1900 if( !menu->hWnd ) return FALSE;
1902 top_popup = menu->hWnd;
1903 top_popup_hmenu = hmenu;
1905 /* Display the window */
1907 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1908 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1909 UpdateWindow( menu->hWnd );
1914 /***********************************************************************
1915 * MENU_EnsureMenuItemVisible
1918 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1920 if (lppop->bScrolling)
1922 MENUITEM *item = &lppop->items[wIndex];
1923 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1924 UINT nOldPos = lppop->nScrollPos;
1926 UINT arrow_bitmap_height;
1929 GetClientRect(lppop->hWnd, &rc);
1931 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1932 arrow_bitmap_height = bmp.bmHeight;
1934 rc.top += arrow_bitmap_height;
1935 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1937 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1938 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1941 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1942 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1943 MENU_DrawScrollArrows(lppop, hdc);
1945 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1947 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1948 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1949 MENU_DrawScrollArrows(lppop, hdc);
1955 /***********************************************************************
1958 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1959 BOOL sendMenuSelect, HMENU topmenu )
1964 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1966 lppop = MENU_GetMenu( hmenu );
1967 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1969 if (lppop->FocusedItem == wIndex) return;
1970 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1971 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1973 top_popup = lppop->hWnd;
1974 top_popup_hmenu = hmenu;
1977 SelectObject( hdc, get_menu_font(FALSE));
1979 /* Clear previous highlighted item */
1980 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1982 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1983 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1984 lppop->Height, !(lppop->wFlags & MF_POPUP),
1988 /* Highlight new item (if any) */
1989 lppop->FocusedItem = wIndex;
1990 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1992 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1993 lppop->items[wIndex].fState |= MF_HILITE;
1994 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1995 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1996 &lppop->items[wIndex], lppop->Height,
1997 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2001 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2002 SendMessageW( hwndOwner, WM_MENUSELECT,
2003 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2004 ip->fType | ip->fState |
2005 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2008 else if (sendMenuSelect) {
2011 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2012 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2013 MENUITEM *ip = &ptm->items[pos];
2014 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2015 ip->fType | ip->fState |
2016 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2020 ReleaseDC( lppop->hWnd, hdc );
2024 /***********************************************************************
2025 * MENU_MoveSelection
2027 * Moves currently selected item according to the offset parameter.
2028 * If there is no selection then it should select the last item if
2029 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2031 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2036 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2038 menu = MENU_GetMenu( hmenu );
2039 if ((!menu) || (!menu->items)) return;
2041 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2043 if( menu->nItems == 1 ) return; else
2044 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2046 if (!(menu->items[i].fType & MF_SEPARATOR))
2048 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2053 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2054 i >= 0 && i < menu->nItems ; i += offset)
2055 if (!(menu->items[i].fType & MF_SEPARATOR))
2057 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2063 /**********************************************************************
2066 * Insert (allocate) a new item into a menu.
2068 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2073 if (!(menu = MENU_GetMenu(hMenu)))
2076 /* Find where to insert new item */
2078 if (flags & MF_BYPOSITION) {
2079 if (pos > menu->nItems)
2082 if (!MENU_FindItem( &hMenu, &pos, flags ))
2085 if (!(menu = MENU_GetMenu( hMenu )))
2090 /* Make sure that MDI system buttons stay on the right side.
2091 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2092 * regardless of their id.
2094 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2095 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2098 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2100 /* Create new items array */
2102 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2105 WARN("allocation failed\n" );
2108 if (menu->nItems > 0)
2110 /* Copy the old array into the new one */
2111 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2112 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2113 (menu->nItems-pos)*sizeof(MENUITEM) );
2114 HeapFree( GetProcessHeap(), 0, menu->items );
2116 menu->items = newItems;
2118 memset( &newItems[pos], 0, sizeof(*newItems) );
2119 menu->Height = 0; /* force size recalculate */
2120 return &newItems[pos];
2124 /**********************************************************************
2125 * MENU_ParseResource
2127 * Parse a standard menu resource and add items to the menu.
2128 * Return a pointer to the end of the resource.
2130 * NOTE: flags is equivalent to the mtOption field
2132 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2140 flags = GET_WORD(res);
2141 end_flag = flags & MF_END;
2142 /* Remove MF_END because it has the same value as MF_HILITE */
2144 res += sizeof(WORD);
2145 if (!(flags & MF_POPUP))
2148 res += sizeof(WORD);
2151 res += (strlenW(str) + 1) * sizeof(WCHAR);
2152 if (flags & MF_POPUP)
2154 HMENU hSubMenu = CreatePopupMenu();
2155 if (!hSubMenu) return NULL;
2156 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2157 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2159 else /* Not a popup */
2161 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2163 } while (!end_flag);
2168 /**********************************************************************
2169 * MENUEX_ParseResource
2171 * Parse an extended menu resource and add items to the menu.
2172 * Return a pointer to the end of the resource.
2174 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2180 mii.cbSize = sizeof(mii);
2181 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2182 mii.fType = GET_DWORD(res);
2183 res += sizeof(DWORD);
2184 mii.fState = GET_DWORD(res);
2185 res += sizeof(DWORD);
2186 mii.wID = GET_DWORD(res);
2187 res += sizeof(DWORD);
2188 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2189 res += sizeof(WORD);
2190 /* Align the text on a word boundary. */
2191 res += (~((UINT_PTR)res - 1)) & 1;
2192 mii.dwTypeData = (LPWSTR) res;
2193 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2194 /* Align the following fields on a dword boundary. */
2195 res += (~((UINT_PTR)res - 1)) & 3;
2197 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2198 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2200 if (resinfo & 1) { /* Pop-up? */
2201 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2202 res += sizeof(DWORD);
2203 mii.hSubMenu = CreatePopupMenu();
2206 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2207 DestroyMenu(mii.hSubMenu);
2210 mii.fMask |= MIIM_SUBMENU;
2211 mii.fType |= MF_POPUP;
2213 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2215 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2216 mii.wID, mii.fType);
2217 mii.fType |= MF_SEPARATOR;
2219 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2220 } while (!(resinfo & MF_END));
2225 /***********************************************************************
2228 * Return the handle of the selected sub-popup menu (if any).
2230 static HMENU MENU_GetSubPopup( HMENU hmenu )
2235 menu = MENU_GetMenu( hmenu );
2237 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2239 item = &menu->items[menu->FocusedItem];
2240 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2241 return item->hSubMenu;
2246 /***********************************************************************
2247 * MENU_HideSubPopups
2249 * Hide the sub-popup menus of this menu.
2251 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2252 BOOL sendMenuSelect, UINT wFlags )
2254 POPUPMENU *menu = MENU_GetMenu( hmenu );
2256 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2258 if (menu && top_popup)
2264 if (menu->FocusedItem != NO_SELECTED_ITEM)
2266 item = &menu->items[menu->FocusedItem];
2267 if (!(item->fType & MF_POPUP) ||
2268 !(item->fState & MF_MOUSESELECT)) return;
2269 item->fState &= ~MF_MOUSESELECT;
2270 hsubmenu = item->hSubMenu;
2273 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2274 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2275 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2276 DestroyWindow( submenu->hWnd );
2279 if (!(wFlags & TPM_NONOTIFY))
2280 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2281 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2286 /***********************************************************************
2289 * Display the sub-menu of the selected item of this menu.
2290 * Return the handle of the submenu, or hmenu if no submenu to display.
2292 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2293 BOOL selectFirst, UINT wFlags )
2300 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2302 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2304 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2306 item = &menu->items[menu->FocusedItem];
2307 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2310 /* message must be sent before using item,
2311 because nearly everything may be changed by the application ! */
2313 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2314 if (!(wFlags & TPM_NONOTIFY))
2315 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2316 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2318 item = &menu->items[menu->FocusedItem];
2321 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2322 if (!(item->fState & MF_HILITE))
2324 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2325 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2327 SelectObject( hdc, get_menu_font(FALSE));
2329 item->fState |= MF_HILITE;
2330 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2331 ReleaseDC( menu->hWnd, hdc );
2333 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2336 item->fState |= MF_MOUSESELECT;
2338 if (IS_SYSTEM_MENU(menu))
2340 MENU_InitSysMenuPopup(item->hSubMenu,
2341 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2342 GetClassLongW( menu->hWnd, GCL_STYLE));
2344 NC_GetSysPopupPos( menu->hWnd, &rect );
2345 rect.top = rect.bottom;
2346 rect.right = GetSystemMetrics(SM_CXSIZE);
2347 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2351 GetWindowRect( menu->hWnd, &rect );
2352 if (menu->wFlags & MF_POPUP)
2354 RECT rc = item->rect;
2356 MENU_AdjustMenuItemRect(menu, &rc);
2358 /* The first item in the popup menu has to be at the
2359 same y position as the focused menu item */
2360 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2361 rect.top += rc.top - MENU_TOP_MARGIN;
2362 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2363 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2364 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2368 rect.left += item->rect.left;
2369 rect.top += item->rect.bottom;
2370 rect.right = item->rect.right - item->rect.left;
2371 rect.bottom = item->rect.bottom - item->rect.top;
2375 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2376 rect.left, rect.top, rect.right, rect.bottom );
2378 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2379 return item->hSubMenu;
2384 /**********************************************************************
2387 HWND MENU_IsMenuActive(void)
2392 /**********************************************************************
2395 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2397 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2399 void MENU_EndMenu( HWND hwnd )
2402 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2403 if (menu && hwnd == menu->hwndOwner) EndMenu();
2406 /***********************************************************************
2409 * Walks menu chain trying to find a menu pt maps to.
2411 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2413 POPUPMENU *menu = MENU_GetMenu( hMenu );
2414 UINT item = menu->FocusedItem;
2417 /* try subpopup first (if any) */
2418 ret = (item != NO_SELECTED_ITEM &&
2419 (menu->items[item].fType & MF_POPUP) &&
2420 (menu->items[item].fState & MF_MOUSESELECT))
2421 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2423 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2425 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2426 if( menu->wFlags & MF_POPUP )
2428 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2430 else if (ht == HTSYSMENU)
2431 ret = get_win_sys_menu( menu->hWnd );
2432 else if (ht == HTMENU)
2433 ret = GetMenu( menu->hWnd );
2438 /***********************************************************************
2439 * MENU_ExecFocusedItem
2441 * Execute a menu item (for instance when user pressed Enter).
2442 * Return the wID of the executed item. Otherwise, -1 indicating
2443 * that no menu item was executed, -2 if a popup is shown;
2444 * Have to receive the flags for the TrackPopupMenu options to avoid
2445 * sending unwanted message.
2448 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2451 POPUPMENU *menu = MENU_GetMenu( hMenu );
2453 TRACE("%p hmenu=%p\n", pmt, hMenu);
2455 if (!menu || !menu->nItems ||
2456 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2458 item = &menu->items[menu->FocusedItem];
2460 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2462 if (!(item->fType & MF_POPUP))
2464 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2466 /* If TPM_RETURNCMD is set you return the id, but
2467 do not send a message to the owner */
2468 if(!(wFlags & TPM_RETURNCMD))
2470 if( menu->wFlags & MF_SYSMENU )
2471 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2472 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2475 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2476 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2478 if (dwStyle & MNS_NOTIFYBYPOS)
2479 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2482 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2490 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2497 /***********************************************************************
2498 * MENU_SwitchTracking
2500 * Helper function for menu navigation routines.
2502 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2504 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2505 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2507 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2509 if( pmt->hTopMenu != hPtMenu &&
2510 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2512 /* both are top level menus (system and menu-bar) */
2513 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2514 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2515 pmt->hTopMenu = hPtMenu;
2517 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2518 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2522 /***********************************************************************
2525 * Return TRUE if we can go on with menu tracking.
2527 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2529 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2534 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2537 if( IS_SYSTEM_MENU(ptmenu) )
2538 item = ptmenu->items;
2540 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2544 if( ptmenu->FocusedItem != id )
2545 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2547 /* If the popup menu is not already "popped" */
2548 if(!(item->fState & MF_MOUSESELECT ))
2550 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2555 /* Else the click was on the menu bar, finish the tracking */
2560 /***********************************************************************
2563 * Return the value of MENU_ExecFocusedItem if
2564 * the selected item was not a popup. Else open the popup.
2565 * A -1 return value indicates that we go on with menu tracking.
2568 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2570 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2575 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2578 if( IS_SYSTEM_MENU(ptmenu) )
2579 item = ptmenu->items;
2581 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2583 if( item && (ptmenu->FocusedItem == id ))
2585 debug_print_menuitem ("FocusedItem: ", item, "");
2587 if( !(item->fType & MF_POPUP) )
2589 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2590 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2591 return executedMenuId;
2594 /* If we are dealing with the top-level menu */
2595 /* and this is a click on an already "popped" item: */
2596 /* Stop the menu tracking and close the opened submenus */
2597 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2600 ptmenu->bTimeToHide = TRUE;
2606 /***********************************************************************
2609 * Return TRUE if we can go on with menu tracking.
2611 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2613 UINT id = NO_SELECTED_ITEM;
2614 POPUPMENU *ptmenu = NULL;
2618 ptmenu = MENU_GetMenu( hPtMenu );
2619 if( IS_SYSTEM_MENU(ptmenu) )
2622 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2625 if( id == NO_SELECTED_ITEM )
2627 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2628 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2631 else if( ptmenu->FocusedItem != id )
2633 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2634 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2640 /***********************************************************************
2643 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2645 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2647 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2650 /* When skipping left, we need to do something special after the
2652 if (vk == VK_LEFT && menu->FocusedItem == 0)
2656 /* When skipping right, for the non-system menu, we need to
2657 handle the last non-special menu item (ie skip any window
2658 icons such as MDI maximize, restore or close) */
2659 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2661 UINT i = menu->FocusedItem + 1;
2662 while (i < menu->nItems) {
2663 if ((menu->items[i].wID >= SC_SIZE &&
2664 menu->items[i].wID <= SC_RESTORE)) {
2668 if (i == menu->nItems) {
2672 /* When skipping right, we need to cater for the system menu */
2673 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2675 if (menu->FocusedItem == (menu->nItems - 1)) {
2682 MDINEXTMENU next_menu;
2687 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2688 next_menu.hmenuNext = 0;
2689 next_menu.hwndNext = 0;
2690 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2692 TRACE("%p [%p] -> %p [%p]\n",
2693 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2695 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2697 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2698 hNewWnd = pmt->hOwnerWnd;
2699 if( IS_SYSTEM_MENU(menu) )
2701 /* switch to the menu bar */
2703 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2707 menu = MENU_GetMenu( hNewMenu );
2708 id = menu->nItems - 1;
2710 /* Skip backwards over any system predefined icons,
2711 eg. MDI close, restore etc icons */
2713 (menu->items[id].wID >= SC_SIZE &&
2714 menu->items[id].wID <= SC_RESTORE)) id--;
2717 else if (style & WS_SYSMENU )
2719 /* switch to the system menu */
2720 hNewMenu = get_win_sys_menu( hNewWnd );
2724 else /* application returned a new menu to switch to */
2726 hNewMenu = next_menu.hmenuNext;
2727 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2729 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2731 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2733 if (style & WS_SYSMENU &&
2734 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2736 /* get the real system menu */
2737 hNewMenu = get_win_sys_menu(hNewWnd);
2739 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2741 /* FIXME: Not sure what to do here;
2742 * perhaps try to track hNewMenu as a popup? */
2744 TRACE(" -- got confused.\n");
2751 if( hNewMenu != pmt->hTopMenu )
2753 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2755 if( pmt->hCurrentMenu != pmt->hTopMenu )
2756 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2759 if( hNewWnd != pmt->hOwnerWnd )
2761 pmt->hOwnerWnd = hNewWnd;
2762 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2765 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2766 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2773 /***********************************************************************
2776 * The idea is not to show the popup if the next input message is
2777 * going to hide it anyway.
2779 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2783 msg.hwnd = pmt->hOwnerWnd;
2785 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2786 pmt->trackFlags |= TF_SKIPREMOVE;
2791 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2792 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2794 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2795 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2796 if( msg.message == WM_KEYDOWN &&
2797 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2799 pmt->trackFlags |= TF_SUSPENDPOPUP;
2806 /* failures go through this */
2807 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2811 /***********************************************************************
2814 * Handle a VK_ESCAPE key event in a menu.
2816 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2818 BOOL bEndMenu = TRUE;
2820 if (pmt->hCurrentMenu != pmt->hTopMenu)
2822 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2824 if (menu->wFlags & MF_POPUP)
2826 HMENU hmenutmp, hmenuprev;
2828 hmenuprev = hmenutmp = pmt->hTopMenu;
2830 /* close topmost popup */
2831 while (hmenutmp != pmt->hCurrentMenu)
2833 hmenuprev = hmenutmp;
2834 hmenutmp = MENU_GetSubPopup( hmenuprev );
2837 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2838 pmt->hCurrentMenu = hmenuprev;
2846 /***********************************************************************
2849 * Handle a VK_LEFT key event in a menu.
2851 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2854 HMENU hmenutmp, hmenuprev;
2857 hmenuprev = hmenutmp = pmt->hTopMenu;
2858 menu = MENU_GetMenu( hmenutmp );
2860 /* Try to move 1 column left (if possible) */
2861 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2862 NO_SELECTED_ITEM ) {
2864 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2869 /* close topmost popup */
2870 while (hmenutmp != pmt->hCurrentMenu)
2872 hmenuprev = hmenutmp;
2873 hmenutmp = MENU_GetSubPopup( hmenuprev );
2876 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2877 pmt->hCurrentMenu = hmenuprev;
2879 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2881 /* move menu bar selection if no more popups are left */
2883 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2884 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2886 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2888 /* A sublevel menu was displayed - display the next one
2889 * unless there is another displacement coming up */
2891 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2892 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2893 pmt->hTopMenu, TRUE, wFlags);
2899 /***********************************************************************
2902 * Handle a VK_RIGHT key event in a menu.
2904 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2907 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2910 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2912 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2913 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2915 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2917 /* If already displaying a popup, try to display sub-popup */
2919 hmenutmp = pmt->hCurrentMenu;
2920 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2922 /* if subpopup was displayed then we are done */
2923 if (hmenutmp != pmt->hCurrentMenu) return;
2926 /* Check to see if there's another column */
2927 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2928 NO_SELECTED_ITEM ) {
2929 TRACE("Going to %d.\n", nextcol );
2930 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2935 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2937 if( pmt->hCurrentMenu != pmt->hTopMenu )
2939 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2940 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2941 } else hmenutmp = 0;
2943 /* try to move to the next item */
2944 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2945 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2947 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2948 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2950 pmt->hTopMenu, TRUE, wFlags);
2954 static void CALLBACK release_capture( BOOL __normal )
2956 set_capture_window( 0, GUI_INMENUMODE, NULL );
2959 /***********************************************************************
2962 * Menu tracking code.
2964 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2965 HWND hwnd, const RECT *lprect )
2970 INT executedMenuId = -1;
2972 BOOL enterIdleSent = FALSE;
2976 mt.hCurrentMenu = hmenu;
2977 mt.hTopMenu = hmenu;
2978 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2982 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2983 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2986 if (!(menu = MENU_GetMenu( hmenu )))
2988 WARN("Invalid menu handle %p\n", hmenu);
2989 SetLastError(ERROR_INVALID_MENU_HANDLE);
2993 if (wFlags & TPM_BUTTONDOWN)
2995 /* Get the result in order to start the tracking or not */
2996 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2997 fEndMenu = !fRemove;
3000 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3002 /* owner may not be visible when tracking a popup, so use the menu itself */
3003 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3004 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3006 __TRY while (!fEndMenu)
3008 menu = MENU_GetMenu( mt.hCurrentMenu );
3009 if (!menu) /* sometimes happens if I do a window manager close */
3012 /* we have to keep the message in the queue until it's
3013 * clear that menu loop is not over yet. */
3017 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3019 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3020 /* remove the message from the queue */
3021 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3027 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3028 enterIdleSent = TRUE;
3029 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3035 /* check if EndMenu() tried to cancel us, by posting this message */
3036 if(msg.message == WM_CANCELMODE)
3038 /* we are now out of the loop */
3041 /* remove the message from the queue */
3042 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3044 /* break out of internal loop, ala ESCAPE */
3048 TranslateMessage( &msg );
3051 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3052 enterIdleSent=FALSE;
3055 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3058 * Use the mouse coordinates in lParam instead of those in the MSG
3059 * struct to properly handle synthetic messages. They are already
3060 * in screen coordinates.
3062 mt.pt.x = (short)LOWORD(msg.lParam);
3063 mt.pt.y = (short)HIWORD(msg.lParam);
3065 /* Find a menu for this mouse event */
3066 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3070 /* no WM_NC... messages in captured state */
3072 case WM_RBUTTONDBLCLK:
3073 case WM_RBUTTONDOWN:
3074 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3076 case WM_LBUTTONDBLCLK:
3077 case WM_LBUTTONDOWN:
3078 /* If the message belongs to the menu, removes it from the queue */
3079 /* Else, end menu tracking */
3080 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3081 fEndMenu = !fRemove;
3085 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3088 /* Check if a menu was selected by the mouse */
3091 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3092 TRACE("executedMenuId %d\n", executedMenuId);
3094 /* End the loop if executedMenuId is an item ID */
3095 /* or if the job was done (executedMenuId = 0). */
3096 fEndMenu = fRemove = (executedMenuId != -1);
3098 /* No menu was selected by the mouse */
3099 /* if the function was called by TrackPopupMenu, continue
3100 with the menu tracking. If not, stop it */
3102 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3107 /* the selected menu item must be changed every time */
3108 /* the mouse moves. */
3111 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3113 } /* switch(msg.message) - mouse */
3115 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3117 fRemove = TRUE; /* Keyboard messages are always removed */
3130 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3131 NO_SELECTED_ITEM, FALSE, 0 );
3132 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3133 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3137 case VK_DOWN: /* If on menu bar, pull-down the menu */
3139 menu = MENU_GetMenu( mt.hCurrentMenu );
3140 if (!(menu->wFlags & MF_POPUP))
3141 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3142 else /* otherwise try to move selection */
3143 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3144 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3148 MENU_KeyLeft( &mt, wFlags );
3152 MENU_KeyRight( &mt, wFlags );
3156 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3162 hi.cbSize = sizeof(HELPINFO);
3163 hi.iContextType = HELPINFO_MENUITEM;
3164 if (menu->FocusedItem == NO_SELECTED_ITEM)
3167 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3168 hi.hItemHandle = hmenu;
3169 hi.dwContextId = menu->dwContextHelpID;
3170 hi.MousePos = msg.pt;
3171 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3178 break; /* WM_KEYDOWN */
3185 if (msg.wParam == '\r' || msg.wParam == ' ')
3187 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3188 fEndMenu = (executedMenuId != -2);
3193 /* Hack to avoid control chars. */
3194 /* We will find a better way real soon... */
3195 if (msg.wParam < 32) break;
3197 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3198 LOWORD(msg.wParam), FALSE );
3199 if (pos == (UINT)-2) fEndMenu = TRUE;
3200 else if (pos == (UINT)-1) MessageBeep(0);
3203 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3205 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3206 fEndMenu = (executedMenuId != -2);
3210 } /* switch(msg.message) - kbd */
3214 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3215 DispatchMessageW( &msg );
3219 if (!fEndMenu) fRemove = TRUE;
3221 /* finally remove message from the queue */
3223 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3224 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3225 else mt.trackFlags &= ~TF_SKIPREMOVE;
3227 __FINALLY( release_capture )
3229 /* If dropdown is still painted and the close box is clicked on
3230 then the menu will be destroyed as part of the DispatchMessage above.
3231 This will then invalidate the menu handle in mt.hTopMenu. We should
3232 check for this first. */
3233 if( IsMenu( mt.hTopMenu ) )
3235 menu = MENU_GetMenu( mt.hTopMenu );
3237 if( IsWindow( mt.hOwnerWnd ) )
3239 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3241 if (menu && (menu->wFlags & MF_POPUP))
3243 DestroyWindow( menu->hWnd );
3246 if (!(wFlags & TPM_NONOTIFY))
3247 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3248 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3250 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3251 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3254 /* Reset the variable for hiding menu */
3255 if( menu ) menu->bTimeToHide = FALSE;
3258 /* The return value is only used by TrackPopupMenu */
3259 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3260 if (executedMenuId == -1) executedMenuId = 0;
3261 return executedMenuId;
3264 /***********************************************************************
3267 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3271 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3275 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3276 if (!(wFlags & TPM_NONOTIFY))
3277 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3279 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3281 if (!(wFlags & TPM_NONOTIFY))
3283 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3284 /* If an app changed/recreated menu bar entries in WM_INITMENU
3285 * menu sizes will be recalculated once the menu created/shown.
3289 /* This makes the menus of applications built with Delphi work.
3290 * It also enables menus to be displayed in more than one window,
3291 * but there are some bugs left that need to be fixed in this case.
3293 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3297 /***********************************************************************
3300 static BOOL MENU_ExitTracking(HWND hWnd)
3302 TRACE("hwnd=%p\n", hWnd);
3304 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3307 top_popup_hmenu = NULL;
3311 /***********************************************************************
3312 * MENU_TrackMouseMenuBar
3314 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3316 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3318 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3319 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3321 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3325 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3326 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3327 MENU_ExitTracking(hWnd);
3332 /***********************************************************************
3333 * MENU_TrackKbdMenuBar
3335 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3337 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3339 UINT uItem = NO_SELECTED_ITEM;
3341 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3343 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3345 /* find window that has a menu */
3347 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3348 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3350 /* check if we have to track a system menu */
3352 hTrackMenu = GetMenu( hwnd );
3353 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3355 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3356 hTrackMenu = get_win_sys_menu( hwnd );
3358 wParam |= HTSYSMENU; /* prevent item lookup */
3361 if (!IsMenu( hTrackMenu )) return;
3363 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3365 if( wChar && wChar != ' ' )
3367 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3368 if ( uItem >= (UINT)(-2) )
3370 if( uItem == (UINT)(-1) ) MessageBeep(0);
3371 /* schedule end of menu tracking */
3372 wFlags |= TF_ENDMENU;
3377 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3379 if (!(wParam & HTSYSMENU) || wChar == ' ')
3381 if( uItem == NO_SELECTED_ITEM )
3382 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3384 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3388 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3389 MENU_ExitTracking( hwnd );
3392 /**********************************************************************
3393 * TrackPopupMenuEx (USER32.@)
3395 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3396 HWND hWnd, LPTPMPARAMS lpTpm )
3400 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3401 hMenu, wFlags, x, y, hWnd, lpTpm,
3402 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3404 /* Parameter check */
3405 /* FIXME: this check is performed several times, here and in the called
3406 functions. That could be optimized */
3407 if (!MENU_GetMenu( hMenu ))
3409 SetLastError( ERROR_INVALID_MENU_HANDLE );
3413 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3415 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3416 if (!(wFlags & TPM_NONOTIFY))
3417 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3419 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3420 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3421 lpTpm ? &lpTpm->rcExclude : NULL );
3422 MENU_ExitTracking(hWnd);
3427 /**********************************************************************
3428 * TrackPopupMenu (USER32.@)
3430 * Like the win32 API, the function return the command ID only if the
3431 * flag TPM_RETURNCMD is on.
3434 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3435 INT nReserved, HWND hWnd, const RECT *lpRect )
3437 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3440 /***********************************************************************
3443 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3445 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3447 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3453 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3454 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3458 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3459 return MA_NOACTIVATE;
3464 BeginPaint( hwnd, &ps );
3465 MENU_DrawPopupMenu( hwnd, ps.hdc,
3466 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3467 EndPaint( hwnd, &ps );
3471 case WM_PRINTCLIENT:
3473 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3474 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3482 /* zero out global pointer in case resident popup window was destroyed. */
3483 if (hwnd == top_popup) {
3485 top_popup_hmenu = NULL;
3493 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3496 SetWindowLongPtrW( hwnd, 0, 0 );
3499 case MM_SETMENUHANDLE:
3500 SetWindowLongPtrW( hwnd, 0, wParam );
3503 case MM_GETMENUHANDLE:
3505 return GetWindowLongPtrW( hwnd, 0 );
3508 return DefWindowProcW( hwnd, message, wParam, lParam );
3514 /***********************************************************************
3515 * MENU_GetMenuBarHeight
3517 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3519 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3520 INT orgX, INT orgY )
3526 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3528 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3530 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3531 SelectObject( hdc, get_menu_font(FALSE));
3532 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3533 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3534 ReleaseDC( hwnd, hdc );
3535 return lppop->Height;
3539 /*******************************************************************
3540 * ChangeMenuA (USER32.@)
3542 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3543 UINT id, UINT flags )
3545 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3546 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3548 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3549 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3551 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3552 flags & MF_BYPOSITION ? pos : id,
3553 flags & ~MF_REMOVE );
3554 /* Default: MF_INSERT */
3555 return InsertMenuA( hMenu, pos, flags, id, data );
3559 /*******************************************************************
3560 * ChangeMenuW (USER32.@)
3562 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3563 UINT id, UINT flags )
3565 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3566 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3568 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3569 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3571 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3572 flags & MF_BYPOSITION ? pos : id,
3573 flags & ~MF_REMOVE );
3574 /* Default: MF_INSERT */
3575 return InsertMenuW( hMenu, pos, flags, id, data );
3579 /*******************************************************************
3580 * CheckMenuItem (USER32.@)
3582 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3587 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3588 ret = item->fState & MF_CHECKED;
3589 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3590 else item->fState &= ~MF_CHECKED;
3595 /**********************************************************************
3596 * EnableMenuItem (USER32.@)
3598 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3604 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3606 /* Get the Popupmenu to access the owner menu */
3607 if (!(menu = MENU_GetMenu(hMenu)))
3610 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3613 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3614 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3616 /* If the close item in the system menu change update the close button */
3617 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3619 if (menu->hSysMenuOwner != 0)
3622 POPUPMENU* parentMenu;
3624 /* Get the parent menu to access*/
3625 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3628 /* Refresh the frame to reflect the change */
3629 GetWindowRect(parentMenu->hWnd, &rc);
3630 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3632 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3640 /*******************************************************************
3641 * GetMenuStringA (USER32.@)
3643 INT WINAPI GetMenuStringA(
3644 HMENU hMenu, /* [in] menuhandle */
3645 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3646 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3647 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3648 UINT wFlags /* [in] MF_ flags */
3652 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3653 if (str && nMaxSiz) str[0] = '\0';
3654 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3655 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3658 if (!item->text) return 0;
3659 if (!str || !nMaxSiz) return strlenW(item->text);
3660 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3662 TRACE("returning %s\n", debugstr_a(str));
3667 /*******************************************************************
3668 * GetMenuStringW (USER32.@)
3670 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3671 LPWSTR str, INT nMaxSiz, UINT wFlags )
3675 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3676 if (str && nMaxSiz) str[0] = '\0';
3677 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3678 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3681 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3682 if( !(item->text)) {
3686 lstrcpynW( str, item->text, nMaxSiz );
3687 TRACE("returning %s\n", debugstr_w(str));
3688 return strlenW(str);
3692 /**********************************************************************
3693 * HiliteMenuItem (USER32.@)
3695 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3699 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3700 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3701 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3702 if (menu->FocusedItem == wItemID) return TRUE;
3703 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3704 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3709 /**********************************************************************
3710 * GetMenuState (USER32.@)
3712 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3715 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3716 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3717 debug_print_menuitem (" item: ", item, "");
3718 if (item->fType & MF_POPUP)
3720 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3721 if (!menu) return -1;
3722 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3726 /* We used to (from way back then) mask the result to 0xff. */
3727 /* I don't know why and it seems wrong as the documented */
3728 /* return flag MF_SEPARATOR is outside that mask. */
3729 return (item->fType | item->fState);
3734 /**********************************************************************
3735 * GetMenuItemCount (USER32.@)
3737 INT WINAPI GetMenuItemCount( HMENU hMenu )
3739 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3740 if (!menu) return -1;
3741 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3742 return menu->nItems;
3746 /**********************************************************************
3747 * GetMenuItemID (USER32.@)
3749 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3753 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3754 if (lpmi->fType & MF_POPUP) return -1;
3760 /**********************************************************************
3763 * Uses flags, id and text ptr, passed by InsertMenu() and
3764 * ModifyMenu() to setup a MenuItemInfo structure.
3766 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3767 LPMENUITEMINFOW pmii)
3769 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3770 pmii->cbSize = sizeof( MENUITEMINFOW);
3771 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3772 /* setting bitmap clears text and vice versa */
3773 if( IS_STRING_ITEM(flags)) {
3774 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3776 flags |= MF_SEPARATOR;
3777 /* Item beginning with a backspace is a help item */
3778 /* FIXME: wrong place, this is only true in win16 */
3779 else if( *str == '\b') {
3783 pmii->dwTypeData = (LPWSTR)str;
3784 } else if( flags & MFT_BITMAP){
3785 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3786 pmii->hbmpItem = ULongToHandle(LOWORD(str));
3788 if( flags & MF_OWNERDRAW){
3789 pmii->fMask |= MIIM_DATA;
3790 pmii->dwItemData = (ULONG_PTR) str;
3792 if( flags & MF_POPUP) {
3793 pmii->fMask |= MIIM_SUBMENU;
3794 pmii->hSubMenu = (HMENU)id;
3796 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3797 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3798 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3799 pmii->wID = (UINT)id;
3803 /*******************************************************************
3804 * InsertMenuW (USER32.@)
3806 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3807 UINT_PTR id, LPCWSTR str )
3812 if (IS_STRING_ITEM(flags) && str)
3813 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3814 hMenu, pos, flags, id, debugstr_w(str) );
3815 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3816 hMenu, pos, flags, id, str );
3818 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3819 MENU_mnu2mnuii( flags, id, str, &mii);
3820 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3822 RemoveMenu( hMenu, pos, flags );
3826 item->hCheckBit = item->hUnCheckBit = 0;
3831 /*******************************************************************
3832 * InsertMenuA (USER32.@)
3834 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3835 UINT_PTR id, LPCSTR str )
3839 if (IS_STRING_ITEM(flags) && str)
3841 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3842 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3845 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3846 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3847 HeapFree( GetProcessHeap(), 0, newstr );
3851 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3855 /*******************************************************************
3856 * AppendMenuA (USER32.@)
3858 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3859 UINT_PTR id, LPCSTR data )
3861 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3865 /*******************************************************************
3866 * AppendMenuW (USER32.@)
3868 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3869 UINT_PTR id, LPCWSTR data )
3871 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3875 /**********************************************************************
3876 * RemoveMenu (USER32.@)
3878 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3883 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3884 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3885 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3889 MENU_FreeItemData( item );
3891 if (--menu->nItems == 0)
3893 HeapFree( GetProcessHeap(), 0, menu->items );
3898 while(nPos < menu->nItems)
3904 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3905 menu->nItems * sizeof(MENUITEM) );
3911 /**********************************************************************
3912 * DeleteMenu (USER32.@)
3914 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3916 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3917 if (!item) return FALSE;
3918 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3919 /* nPos is now the position of the item */
3920 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3925 /*******************************************************************
3926 * ModifyMenuW (USER32.@)
3928 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3929 UINT_PTR id, LPCWSTR str )
3934 if (IS_STRING_ITEM(flags))
3935 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3937 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3939 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3940 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3941 MENU_mnu2mnuii( flags, id, str, &mii);
3942 return SetMenuItemInfo_common( item, &mii, TRUE);
3946 /*******************************************************************
3947 * ModifyMenuA (USER32.@)
3949 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3950 UINT_PTR id, LPCSTR str )
3954 if (IS_STRING_ITEM(flags) && str)
3956 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3957 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3960 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3961 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3962 HeapFree( GetProcessHeap(), 0, newstr );
3966 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3970 /**********************************************************************
3971 * CreatePopupMenu (USER32.@)
3973 HMENU WINAPI CreatePopupMenu(void)
3978 if (!(hmenu = CreateMenu())) return 0;
3979 menu = MENU_GetMenu( hmenu );
3980 menu->wFlags |= MF_POPUP;
3981 menu->bTimeToHide = FALSE;
3986 /**********************************************************************
3987 * GetMenuCheckMarkDimensions (USER.417)
3988 * GetMenuCheckMarkDimensions (USER32.@)
3990 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3992 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3996 /**********************************************************************
3997 * SetMenuItemBitmaps (USER32.@)
3999 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4000 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4004 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4006 if (!hNewCheck && !hNewUnCheck)
4008 item->fState &= ~MF_USECHECKBITMAPS;
4010 else /* Install new bitmaps */
4012 item->hCheckBit = hNewCheck;
4013 item->hUnCheckBit = hNewUnCheck;
4014 item->fState |= MF_USECHECKBITMAPS;
4020 /**********************************************************************
4021 * CreateMenu (USER32.@)
4023 HMENU WINAPI CreateMenu(void)
4028 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4029 menu->FocusedItem = NO_SELECTED_ITEM;
4030 menu->bTimeToHide = FALSE;
4032 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4034 TRACE("return %p\n", hMenu );
4040 /**********************************************************************
4041 * DestroyMenu (USER32.@)
4043 BOOL WINAPI DestroyMenu( HMENU hMenu )
4047 TRACE("(%p)\n", hMenu);
4049 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4050 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4052 /* DestroyMenu should not destroy system menu popup owner */
4053 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4055 DestroyWindow( lppop->hWnd );
4059 if (lppop->items) /* recursively destroy submenus */
4062 MENUITEM *item = lppop->items;
4063 for (i = lppop->nItems; i > 0; i--, item++)
4065 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4066 MENU_FreeItemData( item );
4068 HeapFree( GetProcessHeap(), 0, lppop->items );
4070 HeapFree( GetProcessHeap(), 0, lppop );
4075 /**********************************************************************
4076 * GetSystemMenu (USER32.@)
4078 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4080 WND *wndPtr = WIN_GetPtr( hWnd );
4083 if (wndPtr == WND_DESKTOP) return 0;
4084 if (wndPtr == WND_OTHER_PROCESS)
4086 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4090 if (wndPtr->hSysMenu && bRevert)
4092 DestroyMenu(wndPtr->hSysMenu);
4093 wndPtr->hSysMenu = 0;
4096 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4097 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4099 if( wndPtr->hSysMenu )
4102 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4104 /* Store the dummy sysmenu handle to facilitate the refresh */
4105 /* of the close button if the SC_CLOSE item change */
4106 menu = MENU_GetMenu(retvalue);
4108 menu->hSysMenuOwner = wndPtr->hSysMenu;
4110 WIN_ReleasePtr( wndPtr );
4112 return bRevert ? 0 : retvalue;
4116 /*******************************************************************
4117 * SetSystemMenu (USER32.@)
4119 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4121 WND *wndPtr = WIN_GetPtr( hwnd );
4123 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4125 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4126 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4127 WIN_ReleasePtr( wndPtr );
4134 /**********************************************************************
4135 * GetMenu (USER32.@)
4137 HMENU WINAPI GetMenu( HWND hWnd )
4139 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4140 TRACE("for %p returning %p\n", hWnd, retvalue);
4144 /**********************************************************************
4145 * GetMenuBarInfo (USER32.@)
4147 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4149 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4153 /**********************************************************************
4156 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4157 * SetWindowPos call that would result if SetMenu were called directly.
4159 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4161 TRACE("(%p, %p);\n", hWnd, hMenu);
4163 if (hMenu && !IsMenu(hMenu))
4165 WARN("hMenu %p is not a menu handle\n", hMenu);
4168 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4171 hWnd = WIN_GetFullHandle( hWnd );
4172 if (GetCapture() == hWnd)
4173 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4179 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4181 lpmenu->hWnd = hWnd;
4182 lpmenu->Height = 0; /* Make sure we recalculate the size */
4184 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4189 /**********************************************************************
4190 * SetMenu (USER32.@)
4192 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4194 if(!MENU_SetMenu(hWnd, hMenu))
4197 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4198 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4203 /**********************************************************************
4204 * GetSubMenu (USER32.@)
4206 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4210 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4211 if (!(lpmi->fType & MF_POPUP)) return 0;
4212 return lpmi->hSubMenu;
4216 /**********************************************************************
4217 * DrawMenuBar (USER32.@)
4219 BOOL WINAPI DrawMenuBar( HWND hWnd )
4222 HMENU hMenu = GetMenu(hWnd);
4224 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4226 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4228 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4229 lppop->hwndOwner = hWnd;
4230 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4231 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4235 /***********************************************************************
4236 * DrawMenuBarTemp (USER32.@)
4240 * called by W98SE desk.cpl Control Panel Applet
4242 * Not 100% sure about the param names, but close.
4244 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4249 BOOL flat_menu = FALSE;
4251 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4254 hMenu = GetMenu(hwnd);
4257 hFont = get_menu_font(FALSE);
4259 lppop = MENU_GetMenu( hMenu );
4260 if (lppop == NULL || lprect == NULL)
4262 retvalue = GetSystemMetrics(SM_CYMENU);
4266 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4268 hfontOld = SelectObject( hDC, hFont);
4270 if (lppop->Height == 0)
4271 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4273 lprect->bottom = lprect->top + lppop->Height;
4275 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4277 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4278 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4279 LineTo( hDC, lprect->right, lprect->bottom );
4281 if (lppop->nItems == 0)
4283 retvalue = GetSystemMetrics(SM_CYMENU);
4287 for (i = 0; i < lppop->nItems; i++)
4289 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4290 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4292 retvalue = lppop->Height;
4295 if (hfontOld) SelectObject (hDC, hfontOld);
4299 /***********************************************************************
4300 * EndMenu (USER.187)
4301 * EndMenu (USER32.@)
4303 BOOL WINAPI EndMenu(void)
4305 /* if we are in the menu code, and it is active */
4306 if (!fEndMenu && top_popup)
4308 /* terminate the menu handling code */
4311 /* needs to be posted to wakeup the internal menu handler */
4312 /* which will now terminate the menu, in the event that */
4313 /* the main window was minimized, or lost focus, so we */
4314 /* don't end up with an orphaned menu */
4315 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4321 /*****************************************************************
4322 * LoadMenuA (USER32.@)
4324 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4326 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4327 if (!hrsrc) return 0;
4328 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4332 /*****************************************************************
4333 * LoadMenuW (USER32.@)
4335 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4337 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4338 if (!hrsrc) return 0;
4339 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4343 /**********************************************************************
4344 * LoadMenuIndirectW (USER32.@)
4346 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4349 WORD version, offset;
4350 LPCSTR p = template;
4352 version = GET_WORD(p);
4354 TRACE("%p, ver %d\n", template, version );
4357 case 0: /* standard format is version of 0 */
4358 offset = GET_WORD(p);
4359 p += sizeof(WORD) + offset;
4360 if (!(hMenu = CreateMenu())) return 0;
4361 if (!MENU_ParseResource( p, hMenu ))
4363 DestroyMenu( hMenu );
4367 case 1: /* extended format is version of 1 */
4368 offset = GET_WORD(p);
4369 p += sizeof(WORD) + offset;
4370 if (!(hMenu = CreateMenu())) return 0;
4371 if (!MENUEX_ParseResource( p, hMenu))
4373 DestroyMenu( hMenu );
4378 ERR("version %d not supported.\n", version);
4384 /**********************************************************************
4385 * LoadMenuIndirectA (USER32.@)
4387 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4389 return LoadMenuIndirectW( template );
4393 /**********************************************************************
4396 BOOL WINAPI IsMenu(HMENU hmenu)
4398 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4402 SetLastError(ERROR_INVALID_MENU_HANDLE);
4408 /**********************************************************************
4409 * GetMenuItemInfo_common
4412 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4413 LPMENUITEMINFOW lpmii, BOOL unicode)
4415 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4417 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4420 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4424 if( lpmii->fMask & MIIM_TYPE) {
4425 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4426 WARN("invalid combination of fMask bits used\n");
4427 /* this does not happen on Win9x/ME */
4428 SetLastError( ERROR_INVALID_PARAMETER);
4431 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4432 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4433 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4434 if( lpmii->fType & MFT_BITMAP) {
4435 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4437 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4438 /* this does not happen on Win9x/ME */
4439 lpmii->dwTypeData = 0;
4444 /* copy the text string */
4445 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4447 if(lpmii->dwTypeData && lpmii->cch) {
4450 *((WCHAR *)lpmii->dwTypeData) = 0;
4452 *((CHAR *)lpmii->dwTypeData) = 0;
4458 len = strlenW(menu->text);
4459 if(lpmii->dwTypeData && lpmii->cch)
4460 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4464 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4465 0, NULL, NULL ) - 1;
4466 if(lpmii->dwTypeData && lpmii->cch)
4467 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4468 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4469 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4471 /* if we've copied a substring we return its length */
4472 if(lpmii->dwTypeData && lpmii->cch)
4473 if (lpmii->cch <= len + 1)
4478 /* return length of string */
4479 /* not on Win9x/ME if fType & MFT_BITMAP */
4485 if (lpmii->fMask & MIIM_FTYPE)
4486 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4488 if (lpmii->fMask & MIIM_BITMAP)
4489 lpmii->hbmpItem = menu->hbmpItem;
4491 if (lpmii->fMask & MIIM_STATE)
4492 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4494 if (lpmii->fMask & MIIM_ID)
4495 lpmii->wID = menu->wID;
4497 if (lpmii->fMask & MIIM_SUBMENU)
4498 lpmii->hSubMenu = menu->hSubMenu;
4500 /* hSubMenu is always cleared
4501 * (not on Win9x/ME ) */
4502 lpmii->hSubMenu = 0;
4505 if (lpmii->fMask & MIIM_CHECKMARKS) {
4506 lpmii->hbmpChecked = menu->hCheckBit;
4507 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4509 if (lpmii->fMask & MIIM_DATA)
4510 lpmii->dwItemData = menu->dwItemData;
4515 /**********************************************************************
4516 * GetMenuItemInfoA (USER32.@)
4518 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4519 LPMENUITEMINFOA lpmii)
4523 if( lpmii->cbSize != sizeof( mii) &&
4524 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4525 SetLastError( ERROR_INVALID_PARAMETER);
4528 memcpy( &mii, lpmii, lpmii->cbSize);
4529 mii.cbSize = sizeof( mii);
4530 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4531 (LPMENUITEMINFOW)&mii, FALSE);
4532 mii.cbSize = lpmii->cbSize;
4533 memcpy( lpmii, &mii, mii.cbSize);
4537 /**********************************************************************
4538 * GetMenuItemInfoW (USER32.@)
4540 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4541 LPMENUITEMINFOW lpmii)
4545 if( lpmii->cbSize != sizeof( mii) &&
4546 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4547 SetLastError( ERROR_INVALID_PARAMETER);
4550 memcpy( &mii, lpmii, lpmii->cbSize);
4551 mii.cbSize = sizeof( mii);
4552 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4553 mii.cbSize = lpmii->cbSize;
4554 memcpy( lpmii, &mii, mii.cbSize);
4559 /* set a menu item text from a ASCII or Unicode string */
4560 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4566 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4567 strcpyW( menu->text, text );
4571 LPCSTR str = (LPCSTR)text;
4572 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4573 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4574 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4579 /**********************************************************************
4582 * detect if there are loops in the menu tree (or the depth is too large)
4584 static int MENU_depth( POPUPMENU *pmenu, int depth)
4591 if( depth > MAXMENUDEPTH) return depth;
4592 item = pmenu->items;
4594 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4595 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4597 int bdepth = MENU_depth( psubmenu, depth);
4598 if( bdepth > subdepth) subdepth = bdepth;
4600 if( subdepth > MAXMENUDEPTH)
4601 TRACE("<- hmenu %p\n", item->hSubMenu);
4607 /**********************************************************************
4608 * SetMenuItemInfo_common
4610 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4611 * MIIM_BITMAP and MIIM_STRING flags instead.
4614 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4615 const MENUITEMINFOW *lpmii,
4618 if (!menu) return FALSE;
4620 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4622 if (lpmii->fMask & MIIM_FTYPE ) {
4623 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4624 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4626 if (lpmii->fMask & MIIM_STRING ) {
4627 /* free the string when used */
4628 HeapFree(GetProcessHeap(), 0, menu->text);
4629 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4632 if (lpmii->fMask & MIIM_STATE)
4633 /* Other menu items having MFS_DEFAULT are not converted
4635 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4637 if (lpmii->fMask & MIIM_ID)
4638 menu->wID = lpmii->wID;
4640 if (lpmii->fMask & MIIM_SUBMENU) {
4641 menu->hSubMenu = lpmii->hSubMenu;
4642 if (menu->hSubMenu) {
4643 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4645 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4646 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4650 subMenu->wFlags |= MF_POPUP;
4651 menu->fType |= MF_POPUP;
4653 SetLastError( ERROR_INVALID_PARAMETER);
4658 menu->fType &= ~MF_POPUP;
4661 if (lpmii->fMask & MIIM_CHECKMARKS)
4663 menu->hCheckBit = lpmii->hbmpChecked;
4664 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4666 if (lpmii->fMask & MIIM_DATA)
4667 menu->dwItemData = lpmii->dwItemData;
4669 if (lpmii->fMask & MIIM_BITMAP)
4670 menu->hbmpItem = lpmii->hbmpItem;
4672 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4673 menu->fType |= MFT_SEPARATOR;
4675 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4679 /**********************************************************************
4680 * MENU_NormalizeMenuItemInfoStruct
4682 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4683 * check, copy and extend the MENUITEMINFO struct from the version that the application
4684 * supplied to the version used by wine source. */
4685 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4686 MENUITEMINFOW *pmii_out )
4688 /* do we recognize the size? */
4689 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4690 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4691 SetLastError( ERROR_INVALID_PARAMETER);
4694 /* copy the fields that we have */
4695 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4696 /* if the hbmpItem member is missing then extend */
4697 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4698 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4699 pmii_out->hbmpItem = NULL;
4701 /* test for invalid bit combinations */
4702 if( (pmii_out->fMask & MIIM_TYPE &&
4703 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4704 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4705 WARN("invalid combination of fMask bits used\n");
4706 /* this does not happen on Win9x/ME */
4707 SetLastError( ERROR_INVALID_PARAMETER);
4710 /* convert old style (MIIM_TYPE) to the new */
4711 if( pmii_out->fMask & MIIM_TYPE){
4712 pmii_out->fMask |= MIIM_FTYPE;
4713 if( IS_STRING_ITEM(pmii_out->fType)){
4714 pmii_out->fMask |= MIIM_STRING;
4715 } else if( (pmii_out->fType) & MFT_BITMAP){
4716 pmii_out->fMask |= MIIM_BITMAP;
4717 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4723 /**********************************************************************
4724 * SetMenuItemInfoA (USER32.@)
4726 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4727 const MENUITEMINFOA *lpmii)
4731 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4733 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4735 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4739 /**********************************************************************
4740 * SetMenuItemInfoW (USER32.@)
4742 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4743 const MENUITEMINFOW *lpmii)
4747 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4749 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4750 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4751 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4754 /**********************************************************************
4755 * SetMenuDefaultItem (USER32.@)
4758 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4764 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4766 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4768 /* reset all default-item flags */
4770 for (i = 0; i < menu->nItems; i++, item++)
4772 item->fState &= ~MFS_DEFAULT;
4775 /* no default item */
4784 if ( uItem >= menu->nItems ) return FALSE;
4785 item[uItem].fState |= MFS_DEFAULT;
4790 for (i = 0; i < menu->nItems; i++, item++)
4792 if (item->wID == uItem)
4794 item->fState |= MFS_DEFAULT;
4803 /**********************************************************************
4804 * GetMenuDefaultItem (USER32.@)
4806 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4812 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4814 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4816 /* find default item */
4820 if (! item) return -1;
4822 while ( !( item->fState & MFS_DEFAULT ) )
4825 if (i >= menu->nItems ) return -1;
4828 /* default: don't return disabled items */
4829 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4831 /* search rekursiv when needed */
4832 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4835 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4836 if ( -1 != ret ) return ret;
4838 /* when item not found in submenu, return the popup item */
4840 return ( bypos ) ? i : item->wID;
4845 /**********************************************************************
4846 * InsertMenuItemA (USER32.@)
4848 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4849 const MENUITEMINFOA *lpmii)
4854 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4856 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4858 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4859 return SetMenuItemInfo_common(item, &mii, FALSE);
4863 /**********************************************************************
4864 * InsertMenuItemW (USER32.@)
4866 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4867 const MENUITEMINFOW *lpmii)
4872 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4874 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4876 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4877 return SetMenuItemInfo_common(item, &mii, TRUE);
4880 /**********************************************************************
4881 * CheckMenuRadioItem (USER32.@)
4884 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4885 UINT first, UINT last, UINT check,
4890 MENUITEM *mi_first = NULL, *mi_check;
4891 HMENU m_first, m_check;
4893 for (i = first; i <= last; i++)
4900 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4901 if (!mi_first) continue;
4902 mi_check = mi_first;
4908 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4909 if (!mi_check) continue;
4912 if (m_first != m_check) continue;
4913 if (mi_check->fType == MFT_SEPARATOR) continue;
4917 mi_check->fType |= MFT_RADIOCHECK;
4918 mi_check->fState |= MFS_CHECKED;
4923 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4924 mi_check->fState &= ~MFS_CHECKED;
4932 /**********************************************************************
4933 * GetMenuItemRect (USER32.@)
4935 * ATTENTION: Here, the returned values in rect are the screen
4936 * coordinates of the item just like if the menu was
4937 * always on the upper left side of the application.
4940 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4943 POPUPMENU *itemMenu;
4947 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4949 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4950 referenceHwnd = hwnd;
4954 itemMenu = MENU_GetMenu(hMenu);
4955 if (itemMenu == NULL)
4958 if(itemMenu->hWnd == 0)
4960 referenceHwnd = itemMenu->hWnd;
4963 if ((rect == NULL) || (item == NULL))
4968 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4973 /**********************************************************************
4974 * SetMenuInfo (USER32.@)
4977 * actually use the items to draw the menu
4978 * (recalculate and/or redraw)
4980 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
4983 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
4985 if (lpmi->fMask & MIM_BACKGROUND)
4986 menu->hbrBack = lpmi->hbrBack;
4988 if (lpmi->fMask & MIM_HELPID)
4989 menu->dwContextHelpID = lpmi->dwContextHelpID;
4991 if (lpmi->fMask & MIM_MAXHEIGHT)
4992 menu->cyMax = lpmi->cyMax;
4994 if (lpmi->fMask & MIM_MENUDATA)
4995 menu->dwMenuData = lpmi->dwMenuData;
4997 if (lpmi->fMask & MIM_STYLE)
4998 menu->dwStyle = lpmi->dwStyle;
5000 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5002 MENUITEM *item = menu->items;
5003 for( i = menu->nItems; i; i--, item++)
5004 if( item->fType & MF_POPUP)
5005 menu_SetMenuInfo( item->hSubMenu, lpmi);
5010 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5012 TRACE("(%p %p)\n", hMenu, lpmi);
5013 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5014 if( lpmi->fMask & MIM_STYLE) {
5015 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5016 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5017 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5021 SetLastError( ERROR_INVALID_PARAMETER);
5025 /**********************************************************************
5026 * GetMenuInfo (USER32.@)
5032 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5035 TRACE("(%p %p)\n", hMenu, lpmi);
5037 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5040 if (lpmi->fMask & MIM_BACKGROUND)
5041 lpmi->hbrBack = menu->hbrBack;
5043 if (lpmi->fMask & MIM_HELPID)
5044 lpmi->dwContextHelpID = menu->dwContextHelpID;
5046 if (lpmi->fMask & MIM_MAXHEIGHT)
5047 lpmi->cyMax = menu->cyMax;
5049 if (lpmi->fMask & MIM_MENUDATA)
5050 lpmi->dwMenuData = menu->dwMenuData;
5052 if (lpmi->fMask & MIM_STYLE)
5053 lpmi->dwStyle = menu->dwStyle;
5057 SetLastError( ERROR_INVALID_PARAMETER);
5062 /**********************************************************************
5063 * SetMenuContextHelpId (USER32.@)
5065 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5069 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5071 if ((menu = MENU_GetMenu(hMenu)))
5073 menu->dwContextHelpID = dwContextHelpID;
5080 /**********************************************************************
5081 * GetMenuContextHelpId (USER32.@)
5083 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5087 TRACE("(%p)\n", hMenu);
5089 if ((menu = MENU_GetMenu(hMenu)))
5091 return menu->dwContextHelpID;
5096 /**********************************************************************
5097 * MenuItemFromPoint (USER32.@)
5099 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5101 POPUPMENU *menu = MENU_GetMenu(hMenu);
5104 /*FIXME: Do we have to handle hWnd here? */
5105 if (!menu) return -1;
5106 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5111 /**********************************************************************
5112 * translate_accelerator
5114 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5115 BYTE fVirt, WORD key, WORD cmd )
5120 if (wParam != key) return FALSE;
5122 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5123 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5124 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5126 if (message == WM_CHAR || message == WM_SYSCHAR)
5128 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5130 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5136 if(fVirt & FVIRTKEY)
5138 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5139 wParam, 0xff & HIWORD(lParam));
5141 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5142 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5146 if (!(lParam & 0x01000000)) /* no special_key */
5148 if ((fVirt & FALT) && (lParam & 0x20000000))
5149 { /* ^^ ALT pressed */
5150 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5159 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5163 HMENU hMenu, hSubMenu, hSysMenu;
5164 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5166 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5167 hSysMenu = get_win_sys_menu( hWnd );
5169 /* find menu item and ask application to initialize it */
5170 /* 1. in the system menu */
5171 hSubMenu = hSysMenu;
5173 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5177 if (!IsWindowEnabled(hWnd))
5181 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5182 if(hSubMenu != hSysMenu)
5184 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5185 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5186 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5188 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5191 else /* 2. in the window's menu */
5195 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5199 if (!IsWindowEnabled(hWnd))
5203 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5204 if(hSubMenu != hMenu)
5206 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5207 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5208 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5210 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5217 if (uSysStat != (UINT)-1)
5219 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5226 if (uStat != (UINT)-1)
5232 if (uStat & (MF_DISABLED|MF_GRAYED))
5244 if( mesg==WM_COMMAND )
5246 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5247 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5249 else if( mesg==WM_SYSCOMMAND )
5251 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5252 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5256 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5257 * #0: unknown (please report!)
5258 * #1: for WM_KEYUP,WM_SYSKEYUP
5259 * #2: mouse is captured
5260 * #3: window is disabled
5261 * #4: it's a disabled system menu option
5262 * #5: it's a menu option, but window is iconic
5263 * #6: it's a menu option, but disabled
5265 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5267 ERR_(accel)(" unknown reason - please report!\n");
5272 /**********************************************************************
5273 * TranslateAcceleratorA (USER32.@)
5274 * TranslateAccelerator (USER32.@)
5276 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5278 switch (msg->message)
5282 return TranslateAcceleratorW( hWnd, hAccel, msg );
5288 char ch = LOWORD(msg->wParam);
5290 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5291 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5292 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5300 /**********************************************************************
5301 * TranslateAcceleratorW (USER32.@)
5303 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5305 ACCEL data[32], *ptr = data;
5308 if (!hWnd) return 0;
5310 if (msg->message != WM_KEYDOWN &&
5311 msg->message != WM_SYSKEYDOWN &&
5312 msg->message != WM_CHAR &&
5313 msg->message != WM_SYSCHAR)
5316 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5317 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5319 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5320 if (count > sizeof(data)/sizeof(data[0]))
5322 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5324 count = CopyAcceleratorTableW( hAccel, ptr, count );
5325 for (i = 0; i < count; i++)
5327 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5328 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5331 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );