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/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu);
66 WINE_DECLARE_DEBUG_CHANNEL(accel);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType; /* Item type. */
77 UINT fState; /* Item state. */
78 UINT_PTR wID; /* Item id. */
79 HMENU hSubMenu; /* Pop-up menu. */
80 HBITMAP hCheckBit; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
82 LPWSTR text; /* Item text. */
83 ULONG_PTR dwItemData; /* Application defined. */
84 LPWSTR dwTypeData; /* depends on fMask */
85 HBITMAP hbmpItem; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect; /* Item area (relative to menu window) */
88 UINT xTab; /* X position of text after Tab */
89 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
93 /* Popup menu structure */
95 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic; /* Magic number */
97 WORD Width; /* Width of the whole menu */
98 WORD Height; /* Height of the whole menu */
99 UINT nItems; /* Number of items in the menu */
100 HWND hWnd; /* Window containing the menu */
101 MENUITEM *items; /* Array of menu items */
102 UINT FocusedItem; /* Currently focused item */
103 HWND hwndOwner; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling; /* Scroll arrows are active */
106 UINT nScrollPos; /* Current scroll position */
107 UINT nTotalHeight; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle; /* Extended menu style */
110 UINT cyMax; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack; /* brush for menu background */
112 DWORD dwContextHelpID;
113 DWORD dwMenuData; /* application defined value */
114 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize; /* Maximum size of the bitmap items */
116 } POPUPMENU, *LPPOPUPMENU;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
127 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu; /* initial menu */
129 HWND hOwnerWnd; /* where notifications are sent */
133 #define MENU_MAGIC 0x554d /* 'MU' */
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
141 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
142 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
144 /* Space between 2 columns */
145 #define MENU_COL_SPACE 4
147 /* top and bottom margins for popup menus */
148 #define MENU_TOP_MARGIN 3
149 #define MENU_BOTTOM_MARGIN 2
151 /* (other menu->FocusedItem values give the position of the focused item) */
152 #define NO_SELECTED_ITEM 0xffff
154 #define MENU_ITEM_TYPE(flags) \
155 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
157 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
158 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
159 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
161 #define IS_SYSTEM_MENU(menu) \
162 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
164 #define MENUITEMINFO_TYPE_MASK \
165 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
166 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
167 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
168 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
169 #define STATE_MASK (~TYPE_MASK)
170 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
172 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
174 static SIZE menucharsize;
175 static UINT ODitemheight; /* default owner drawn item height */
177 /* Use global popup window because there's no way 2 menus can
178 * be tracked at the same time. */
179 static HWND top_popup;
181 /* Flag set by EndMenu() to force an exit from menu tracking */
182 static BOOL fEndMenu = FALSE;
184 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class =
193 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
194 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
195 NULL, /* procA (winproc is Unicode only) */
196 PopupMenuWndProc, /* procW */
197 sizeof(HMENU), /* extra */
198 IDC_ARROW, /* cursor */
199 (HBRUSH)(COLOR_MENU+1) /* brush */
203 /***********************************************************************
204 * debug_print_menuitem
206 * Print a menuitem in readable form.
209 #define debug_print_menuitem(pre, mp, post) \
210 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
212 #define MENUOUT(text) \
213 TRACE("%s%s", (count++ ? "," : ""), (text))
215 #define MENUFLAG(bit,text) \
217 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
220 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
223 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
224 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
225 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
226 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
227 TRACE("%s ", prefix);
229 UINT flags = mp->fType;
230 TRACE( "{ ID=0x%lx", mp->wID);
232 TRACE( ", Sub=%p", mp->hSubMenu);
236 MENUFLAG( MFT_SEPARATOR, "sep");
237 MENUFLAG( MFT_OWNERDRAW, "own");
238 MENUFLAG( MFT_BITMAP, "bit");
239 MENUFLAG(MF_POPUP, "pop");
240 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
241 MENUFLAG(MFT_MENUBREAK, "brk");
242 MENUFLAG(MFT_RADIOCHECK, "radio");
243 MENUFLAG(MFT_RIGHTORDER, "rorder");
244 MENUFLAG(MF_SYSMENU, "sys");
245 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 TRACE( "+0x%x", flags);
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
261 TRACE( "+0x%x", flags);
264 TRACE( ", Chk=%p", mp->hCheckBit);
266 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 TRACE( ", Text=%s", debugstr_w(mp->text));
270 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
273 if( IS_MAGIC_BITMAP(mp->hbmpItem))
274 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 TRACE( ", hbitmap=%p", mp->hbmpItem);
281 TRACE(" %s\n", postfix);
288 /***********************************************************************
291 * Validate the given menu handle and returns the menu structure pointer.
293 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
295 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
296 if (!menu || menu->wMagic != MENU_MAGIC)
298 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
304 /***********************************************************************
307 * Get the system menu of a window
309 static HMENU get_win_sys_menu( HWND hwnd )
312 WND *win = WIN_GetPtr( hwnd );
313 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
316 WIN_ReleasePtr( win );
321 /***********************************************************************
324 static HFONT get_menu_font( BOOL bold )
326 static HFONT hMenuFont, hMenuFontBold;
328 HFONT ret = bold ? hMenuFontBold : hMenuFont;
332 NONCLIENTMETRICSW ncm;
335 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
336 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
340 ncm.lfMenuFont.lfWeight += 300;
341 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
343 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
344 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
348 /* another thread beat us to it */
356 /***********************************************************************
359 static HBITMAP get_arrow_bitmap(void)
361 static HBITMAP arrow_bitmap;
363 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
367 /***********************************************************************
368 * get_down_arrow_bitmap
370 static HBITMAP get_down_arrow_bitmap(void)
372 static HBITMAP arrow_bitmap;
374 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
378 /***********************************************************************
379 * get_down_arrow_inactive_bitmap
381 static HBITMAP get_down_arrow_inactive_bitmap(void)
383 static HBITMAP arrow_bitmap;
385 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
389 /***********************************************************************
390 * get_up_arrow_bitmap
392 static HBITMAP get_up_arrow_bitmap(void)
394 static HBITMAP arrow_bitmap;
396 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
400 /***********************************************************************
401 * get_up_arrow_inactive_bitmap
403 static HBITMAP get_up_arrow_inactive_bitmap(void)
405 static HBITMAP arrow_bitmap;
407 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
411 /***********************************************************************
414 * Return the default system menu.
416 static HMENU MENU_CopySysPopup(void)
418 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
419 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
423 MENUITEMINFOW miteminfo;
424 POPUPMENU* menu = MENU_GetMenu(hMenu);
425 menu->wFlags |= MF_SYSMENU | MF_POPUP;
426 minfo.cbSize = sizeof( MENUINFO);
427 minfo.dwStyle = MNS_CHECKORBMP;
428 minfo.fMask = MIM_STYLE;
429 SetMenuInfo( hMenu, &minfo);
430 miteminfo.cbSize = sizeof( MENUITEMINFOW);
431 miteminfo.fMask = MIIM_BITMAP;
432 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
433 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
434 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
435 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
436 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
437 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
438 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
439 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
440 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
443 ERR("Unable to load default system menu\n" );
445 TRACE("returning %p.\n", hMenu );
451 /**********************************************************************
454 * Create a copy of the system menu. System menu in Windows is
455 * a special menu bar with the single entry - system menu popup.
456 * This popup is presented to the outside world as a "system menu".
457 * However, the real system menu handle is sometimes seen in the
458 * WM_MENUSELECT parameters (and Word 6 likes it this way).
460 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
464 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
465 if ((hMenu = CreateMenu()))
467 POPUPMENU *menu = MENU_GetMenu(hMenu);
468 menu->wFlags = MF_SYSMENU;
469 menu->hWnd = WIN_GetFullHandle( hWnd );
470 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
473 hPopupMenu = MENU_CopySysPopup();
477 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
478 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
480 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
481 (UINT_PTR)hPopupMenu, NULL );
483 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
484 menu->items[0].fState = 0;
485 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
487 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
490 DestroyMenu( hMenu );
492 ERR("failed to load system menu!\n");
497 /***********************************************************************
498 * MENU_InitSysMenuPopup
500 * Grey the appropriate items in System menu.
502 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
506 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
507 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
508 gray = ((style & WS_MAXIMIZE) != 0);
509 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
510 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
511 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
513 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
514 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
515 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = (clsStyle & CS_NOCLOSE) != 0;
518 /* The menu item must keep its state if it's disabled */
520 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
524 /******************************************************************************
526 * UINT MENU_GetStartOfNextColumn(
529 *****************************************************************************/
531 static UINT MENU_GetStartOfNextColumn(
534 POPUPMENU *menu = MENU_GetMenu(hMenu);
538 return NO_SELECTED_ITEM;
540 i = menu->FocusedItem + 1;
541 if( i == NO_SELECTED_ITEM )
544 for( ; i < menu->nItems; ++i ) {
545 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
549 return NO_SELECTED_ITEM;
553 /******************************************************************************
555 * UINT MENU_GetStartOfPrevColumn(
558 *****************************************************************************/
560 static UINT MENU_GetStartOfPrevColumn(
563 POPUPMENU *menu = MENU_GetMenu(hMenu);
567 return NO_SELECTED_ITEM;
569 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
570 return NO_SELECTED_ITEM;
572 /* Find the start of the column */
574 for(i = menu->FocusedItem; i != 0 &&
575 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
579 return NO_SELECTED_ITEM;
581 for(--i; i != 0; --i) {
582 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
586 TRACE("ret %d.\n", i );
593 /***********************************************************************
596 * Find a menu item. Return a pointer on the item, and modifies *hmenu
597 * in case the item was in a sub-menu.
599 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
602 MENUITEM *fallback = NULL;
603 UINT fallback_pos = 0;
606 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
607 if (wFlags & MF_BYPOSITION)
609 if (*nPos >= menu->nItems) return NULL;
610 return &menu->items[*nPos];
614 MENUITEM *item = menu->items;
615 for (i = 0; i < menu->nItems; i++, item++)
617 if (item->fType & MF_POPUP)
619 HMENU hsubmenu = item->hSubMenu;
620 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
626 else if (item->wID == *nPos)
628 /* fallback to this item if nothing else found */
633 else if (item->wID == *nPos)
642 *nPos = fallback_pos;
647 /***********************************************************************
650 * Find a Sub menu. Return the position of the submenu, and modifies
651 * *hmenu in case it is found in another sub-menu.
652 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
654 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
659 if (((*hmenu)==(HMENU)0xffff) ||
660 (!(menu = MENU_GetMenu(*hmenu))))
661 return NO_SELECTED_ITEM;
663 for (i = 0; i < menu->nItems; i++, item++) {
664 if(!(item->fType & MF_POPUP)) continue;
665 if (item->hSubMenu == hSubTarget) {
669 HMENU hsubmenu = item->hSubMenu;
670 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
671 if (pos != NO_SELECTED_ITEM) {
677 return NO_SELECTED_ITEM;
680 /***********************************************************************
683 static void MENU_FreeItemData( MENUITEM* item )
686 HeapFree( GetProcessHeap(), 0, item->text );
689 /***********************************************************************
690 * MENU_AdjustMenuItemRect
692 * Adjust menu item rectangle according to scrolling state.
695 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
697 if (menu->bScrolling)
699 UINT arrow_bitmap_height;
702 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
703 arrow_bitmap_height = bmp.bmHeight;
704 rect->top += arrow_bitmap_height - menu->nScrollPos;
705 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
710 /***********************************************************************
711 * MENU_FindItemByCoords
713 * Find the item at the specified coordinates (screen coords). Does
714 * not work for child windows and therefore should not be called for
715 * an arbitrary system menu.
717 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
718 POINT pt, UINT *pos )
724 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
728 for (i = 0; i < menu->nItems; i++, item++)
731 MENU_AdjustMenuItemRect(menu, &rect);
732 if (PtInRect(&rect, pt))
742 /***********************************************************************
745 * Find the menu item selected by a key press.
746 * Return item id, -1 if none, -2 if we should close the menu.
748 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
749 WCHAR key, BOOL forceMenuChar )
751 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
753 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
757 POPUPMENU *menu = MENU_GetMenu( hmenu );
758 MENUITEM *item = menu->items;
765 for (i = 0; i < menu->nItems; i++, item++)
769 WCHAR *p = item->text - 2;
772 p = strchrW (p + 2, '&');
774 while (p != NULL && p [1] == '&');
775 if (p && (toupperW(p[1]) == toupperW(key))) return i;
779 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
780 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
781 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
782 if (HIWORD(menuchar) == 1) return (UINT)(-2);
788 /***********************************************************************
789 * MENU_GetBitmapItemSize
791 * Get the size of a bitmap item.
793 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
797 HBITMAP bmp = lpitem->hbmpItem;
799 size->cx = size->cy = 0;
801 /* check if there is a magic menu item associated with this item */
802 switch( (INT_PTR) bmp )
804 case (INT_PTR)HBMMENU_CALLBACK:
806 MEASUREITEMSTRUCT measItem;
807 measItem.CtlType = ODT_MENU;
809 measItem.itemID = lpitem->wID;
810 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
811 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
812 measItem.itemData = lpitem->dwItemData;
813 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
814 size->cx = measItem.itemWidth;
815 size->cy = measItem.itemHeight;
819 case (INT_PTR)HBMMENU_SYSTEM:
820 if (lpitem->dwItemData)
822 bmp = (HBITMAP)lpitem->dwItemData;
826 case (INT_PTR)HBMMENU_MBAR_RESTORE:
827 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
828 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
829 case (INT_PTR)HBMMENU_MBAR_CLOSE:
830 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
831 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
834 case (INT_PTR)HBMMENU_POPUP_CLOSE:
835 case (INT_PTR)HBMMENU_POPUP_RESTORE:
836 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
837 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
838 size->cx = GetSystemMetrics( SM_CYMENU ) - 4; /* FIXME: test */
842 if (GetObjectW(bmp, sizeof(bm), &bm ))
844 size->cx = bm.bmWidth;
845 size->cy = bm.bmHeight;
849 /***********************************************************************
850 * MENU_DrawBitmapItem
852 * Draw a bitmap item.
854 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
855 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
861 int w = rect->right - rect->left;
862 int h = rect->bottom - rect->top;
865 HBITMAP hbmToDraw = lpitem->hbmpItem;
868 /* Check if there is a magic menu item associated with this item */
869 if (IS_MAGIC_BITMAP(hbmToDraw))
875 switch((INT_PTR)hbmToDraw)
877 case (INT_PTR)HBMMENU_SYSTEM:
878 if (lpitem->dwItemData)
880 bmp = (HBITMAP)lpitem->dwItemData;
881 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
885 static HBITMAP hBmpSysMenu;
887 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
890 /* only use right half of the bitmap */
891 bmp_xoffset = bm.bmWidth / 2;
892 bm.bmWidth -= bmp_xoffset;
895 case (INT_PTR)HBMMENU_MBAR_RESTORE:
896 flags = DFCS_CAPTIONRESTORE;
898 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
899 flags = DFCS_CAPTIONMIN;
901 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
902 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
904 case (INT_PTR)HBMMENU_MBAR_CLOSE:
905 flags = DFCS_CAPTIONCLOSE;
907 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
908 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
910 case (INT_PTR)HBMMENU_CALLBACK:
912 DRAWITEMSTRUCT drawItem;
913 drawItem.CtlType = ODT_MENU;
915 drawItem.itemID = lpitem->wID;
916 drawItem.itemAction = odaction;
917 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
918 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
919 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
920 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
921 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
922 drawItem.hwndItem = (HWND)hmenu;
924 drawItem.itemData = lpitem->dwItemData;
925 drawItem.rcItem = *rect;
926 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
930 case (INT_PTR)HBMMENU_POPUP_CLOSE:
933 case (INT_PTR)HBMMENU_POPUP_RESTORE:
936 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
939 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
943 FIXME("Magic %p not implemented\n", hbmToDraw);
948 HFONT hfont, hfontsav;
949 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
950 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
951 { 'M','a','r','l','e','t','t',0 } };
952 logfont.lfHeight = min( h, w) - 2 ;
953 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
954 hfont = CreateFontIndirectW( &logfont);
955 hfontsav = SelectObject(hdc, hfont);
956 TextOutW( hdc, rect->left, rect->top, &bmchr, 1);
957 SelectObject(hdc, hfontsav);
958 DeleteObject( hfont);
963 InflateRect( &r, -1, -1 );
964 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
965 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
970 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
973 hdcMem = CreateCompatibleDC( hdc );
974 SelectObject( hdcMem, bmp );
976 /* handle fontsize > bitmap_height */
977 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
979 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
980 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
981 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
982 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
987 /***********************************************************************
990 * Calculate the size of the menu item and store it in lpitem->rect.
992 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
993 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
996 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
997 UINT arrow_bitmap_width;
1001 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1002 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1003 (menuBar ? " (MenuBar)" : ""));
1005 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1006 arrow_bitmap_width = bm.bmWidth;
1008 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1009 if( !menucharsize.cx ) {
1010 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1011 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1012 * but it is unlikely an application will depend on that */
1013 ODitemheight = HIWORD( GetDialogBaseUnits());
1016 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1018 if (lpitem->fType & MF_OWNERDRAW)
1020 MEASUREITEMSTRUCT mis;
1021 mis.CtlType = ODT_MENU;
1023 mis.itemID = lpitem->wID;
1024 mis.itemData = lpitem->dwItemData;
1025 mis.itemHeight = ODitemheight;
1027 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1028 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1029 * width of a menufont character to the width of an owner-drawn menu.
1031 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1033 /* under at least win95 you seem to be given a standard
1034 height for the menu and the height value is ignored */
1035 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1037 lpitem->rect.bottom += mis.itemHeight;
1039 TRACE("id=%04lx size=%dx%d\n",
1040 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1041 lpitem->rect.bottom-lpitem->rect.top);
1045 if (lpitem->fType & MF_SEPARATOR)
1047 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1049 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1057 if (lpitem->hbmpItem) {
1060 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1061 /* Keep the size of the bitmap in callback mode to be able
1062 * to draw it correctly */
1063 lpitem->bmpsize = size;
1064 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1065 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1066 lpitem->rect.right += size.cx + 2;
1067 itemheight = size.cy + 2;
1069 if( !(lppop->dwStyle & MNS_NOCHECK))
1070 lpitem->rect.right += check_bitmap_width;
1071 lpitem->rect.right += 4 + menucharsize.cx;
1072 lpitem->xTab = lpitem->rect.right;
1073 lpitem->rect.right += arrow_bitmap_width;
1074 } else if (lpitem->hbmpItem) { /* menuBar */
1077 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1078 lpitem->bmpsize = size;
1079 lpitem->rect.right += size.cx;
1080 if( lpitem->text) lpitem->rect.right += 2;
1081 itemheight = size.cy;
1084 /* it must be a text item - unless it's the system menu */
1085 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1086 HFONT hfontOld = NULL;
1087 RECT rc = lpitem->rect;
1088 LONG txtheight, txtwidth;
1090 if ( lpitem->fState & MFS_DEFAULT ) {
1091 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1094 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1095 DT_SINGLELINE|DT_CALCRECT);
1096 lpitem->rect.right += rc.right - rc.left;
1097 itemheight = max( max( itemheight, txtheight),
1098 GetSystemMetrics( SM_CYMENU) - 1);
1099 lpitem->rect.right += 2 * menucharsize.cx;
1101 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1104 int n = (int)( p - lpitem->text);
1105 /* Item contains a tab (only meaningful in popup menus) */
1106 /* get text size before the tab */
1107 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1108 DT_SINGLELINE|DT_CALCRECT);
1109 txtwidth = rc.right - rc.left;
1110 p += 1; /* advance past the Tab */
1111 /* get text size after the tab */
1112 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1113 DT_SINGLELINE|DT_CALCRECT);
1114 lpitem->xTab += txtwidth;
1115 txtheight = max( txtheight, tmpheight);
1116 txtwidth += menucharsize.cx + /* space for the tab */
1117 tmprc.right - tmprc.left; /* space for the short cut */
1119 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1120 DT_SINGLELINE|DT_CALCRECT);
1121 txtwidth = rc.right - rc.left;
1122 lpitem->xTab += txtwidth;
1124 lpitem->rect.right += 2 + txtwidth;
1125 itemheight = max( itemheight,
1126 max( txtheight + 2, menucharsize.cy + 4));
1128 if (hfontOld) SelectObject (hdc, hfontOld);
1129 } else if( menuBar) {
1130 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1132 lpitem->rect.bottom += itemheight;
1133 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1137 /***********************************************************************
1138 * MENU_GetMaxPopupHeight
1141 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1144 return lppop->cyMax;
1145 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1149 /***********************************************************************
1150 * MENU_PopupMenuCalcSize
1152 * Calculate the size of a popup menu.
1154 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1159 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1161 lppop->Width = lppop->Height = 0;
1162 if (lppop->nItems == 0) return;
1165 SelectObject( hdc, get_menu_font(FALSE));
1170 lppop->maxBmpSize.cx = 0;
1171 lppop->maxBmpSize.cy = 0;
1173 while (start < lppop->nItems)
1175 lpitem = &lppop->items[start];
1177 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1178 orgX += MENU_COL_SPACE;
1179 orgY = MENU_TOP_MARGIN;
1181 maxTab = maxTabWidth = 0;
1182 /* Parse items until column break or end of menu */
1183 for (i = start; i < lppop->nItems; i++, lpitem++)
1186 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1188 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1189 maxX = max( maxX, lpitem->rect.right );
1190 orgY = lpitem->rect.bottom;
1191 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1193 maxTab = max( maxTab, lpitem->xTab );
1194 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1198 /* Finish the column (set all items to the largest width found) */
1199 maxX = max( maxX, maxTab + maxTabWidth );
1200 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1202 lpitem->rect.right = maxX;
1203 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1204 lpitem->xTab = maxTab;
1207 lppop->Height = max( lppop->Height, orgY );
1210 lppop->Width = maxX;
1212 /* space for 3d border */
1213 lppop->Height += MENU_BOTTOM_MARGIN;
1216 /* Adjust popup height if it exceeds maximum */
1217 maxHeight = MENU_GetMaxPopupHeight(lppop);
1218 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1219 if (lppop->Height >= maxHeight)
1221 lppop->Height = maxHeight;
1222 lppop->bScrolling = TRUE;
1226 lppop->bScrolling = FALSE;
1229 ReleaseDC( 0, hdc );
1233 /***********************************************************************
1234 * MENU_MenuBarCalcSize
1236 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1237 * height is off by 1 pixel which causes lengthy window relocations when
1238 * active document window is maximized/restored.
1240 * Calculate the size of the menu bar.
1242 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1243 LPPOPUPMENU lppop, HWND hwndOwner )
1246 UINT start, i, helpPos;
1247 int orgX, orgY, maxY;
1249 if ((lprect == NULL) || (lppop == NULL)) return;
1250 if (lppop->nItems == 0) return;
1251 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1252 lppop->Width = lprect->right - lprect->left;
1254 maxY = lprect->top+1;
1257 lppop->maxBmpSize.cx = 0;
1258 lppop->maxBmpSize.cy = 0;
1259 while (start < lppop->nItems)
1261 lpitem = &lppop->items[start];
1262 orgX = lprect->left;
1265 /* Parse items until line break or end of menu */
1266 for (i = start; i < lppop->nItems; i++, lpitem++)
1268 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1270 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1272 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1273 debug_print_menuitem (" item: ", lpitem, "");
1274 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1276 if (lpitem->rect.right > lprect->right)
1278 if (i != start) break;
1279 else lpitem->rect.right = lprect->right;
1281 maxY = max( maxY, lpitem->rect.bottom );
1282 orgX = lpitem->rect.right;
1285 /* Finish the line (set all items to the largest height found) */
1286 while (start < i) lppop->items[start++].rect.bottom = maxY;
1289 lprect->bottom = maxY;
1290 lppop->Height = lprect->bottom - lprect->top;
1292 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1293 /* the last item (if several lines, only move the last line) */
1294 if (helpPos == ~0U) return;
1295 lpitem = &lppop->items[lppop->nItems-1];
1296 orgY = lpitem->rect.top;
1297 orgX = lprect->right;
1298 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1299 if (lpitem->rect.top != orgY) break; /* Other line */
1300 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1301 lpitem->rect.left += orgX - lpitem->rect.right;
1302 lpitem->rect.right = orgX;
1303 orgX = lpitem->rect.left;
1308 /***********************************************************************
1309 * MENU_DrawScrollArrows
1311 * Draw scroll arrows.
1314 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1316 HDC hdcMem = CreateCompatibleDC(hdc);
1317 HBITMAP hOrigBitmap;
1318 UINT arrow_bitmap_width, arrow_bitmap_height;
1322 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1323 arrow_bitmap_width = bmp.bmWidth;
1324 arrow_bitmap_height = bmp.bmHeight;
1327 if (lppop->nScrollPos)
1328 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1330 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1333 rect.right = lppop->Width;
1334 rect.bottom = arrow_bitmap_height;
1335 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1336 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1337 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1338 rect.top = lppop->Height - arrow_bitmap_height;
1339 rect.bottom = lppop->Height;
1340 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1341 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1342 SelectObject(hdcMem, get_down_arrow_bitmap());
1344 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1345 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1346 lppop->Height - arrow_bitmap_height,
1347 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1348 SelectObject(hdcMem, hOrigBitmap);
1353 /***********************************************************************
1356 * Draws the popup-menu arrow.
1358 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1359 UINT arrow_bitmap_height)
1361 HDC hdcMem = CreateCompatibleDC( hdc );
1362 HBITMAP hOrigBitmap;
1364 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1365 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1366 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1367 arrow_bitmap_width, arrow_bitmap_height,
1368 hdcMem, 0, 0, SRCCOPY );
1369 SelectObject( hdcMem, hOrigBitmap );
1372 /***********************************************************************
1375 * Draw a single menu item.
1377 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1378 UINT height, BOOL menuBar, UINT odaction )
1381 BOOL flat_menu = FALSE;
1383 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1384 POPUPMENU *menu = MENU_GetMenu(hmenu);
1387 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1391 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1392 arrow_bitmap_width = bmp.bmWidth;
1393 arrow_bitmap_height = bmp.bmHeight;
1396 if (lpitem->fType & MF_SYSMENU)
1398 if( !IsIconic(hwnd) )
1399 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1403 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1404 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1408 if (lpitem->fState & MF_HILITE)
1410 if(menuBar && !flat_menu) {
1411 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1412 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1414 if(lpitem->fState & MF_GRAYED)
1415 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1417 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1418 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1423 if (lpitem->fState & MF_GRAYED)
1424 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1426 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1427 SetBkColor( hdc, GetSysColor( bkgnd ) );
1430 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1431 rect = lpitem->rect;
1432 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1434 if (lpitem->fType & MF_OWNERDRAW)
1437 ** Experimentation under Windows reveals that an owner-drawn
1438 ** menu is given the rectangle which includes the space it requested
1439 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1440 ** and a popup-menu arrow. This is the value of lpitem->rect.
1441 ** Windows will leave all drawing to the application except for
1442 ** the popup-menu arrow. Windows always draws that itself, after
1443 ** the menu owner has finished drawing.
1447 dis.CtlType = ODT_MENU;
1449 dis.itemID = lpitem->wID;
1450 dis.itemData = lpitem->dwItemData;
1452 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1453 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1454 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1455 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1456 dis.hwndItem = (HWND)hmenu;
1459 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1460 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1461 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1462 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1463 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1464 /* Draw the popup-menu arrow */
1465 if (lpitem->fType & MF_POPUP)
1466 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1467 arrow_bitmap_height);
1471 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1473 if (lpitem->fState & MF_HILITE)
1477 InflateRect (&rect, -1, -1);
1478 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1479 InflateRect (&rect, 1, 1);
1480 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1485 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1487 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1491 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1493 SetBkMode( hdc, TRANSPARENT );
1495 /* vertical separator */
1496 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1501 rc.left -= MENU_COL_SPACE / 2 + 1;
1503 rc.bottom = height - 3;
1506 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1507 MoveToEx( hdc, rc.left, rc.top, NULL );
1508 LineTo( hdc, rc.left, rc.bottom );
1509 SelectObject( hdc, oldPen );
1512 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1515 /* horizontal separator */
1516 if (lpitem->fType & MF_SEPARATOR)
1523 rc.top = ( rc.top + rc.bottom) / 2;
1526 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1527 MoveToEx( hdc, rc.left, rc.top, NULL );
1528 LineTo( hdc, rc.right, rc.top );
1529 SelectObject( hdc, oldPen );
1532 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1536 /* helper lines for debugging */
1537 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1538 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1539 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1540 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1543 if (lpitem->hbmpItem) {
1544 /* calculate the bitmap rectangle in coordinates relative
1545 * to the item rectangle */
1547 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1550 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1552 else if (menu->dwStyle & MNS_NOCHECK)
1554 else if (menu->dwStyle & MNS_CHECKORBMP)
1557 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1558 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1559 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1562 bmprc.top = (rect.bottom - rect.top -
1563 lpitem->bmpsize.cy) / 2;
1564 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1570 INT y = rect.top + rect.bottom;
1572 int checked = FALSE;
1573 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1574 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1575 /* Draw the check mark
1578 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1580 if( !(menu->dwStyle & MNS_NOCHECK)) {
1581 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1582 lpitem->hUnCheckBit;
1583 if (bm) /* we have a custom bitmap */
1585 HDC hdcMem = CreateCompatibleDC( hdc );
1587 SelectObject( hdcMem, bm );
1588 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1589 check_bitmap_width, check_bitmap_height,
1590 hdcMem, 0, 0, SRCCOPY );
1594 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1597 HBITMAP bm = CreateBitmap( check_bitmap_width,
1598 check_bitmap_height, 1, 1, NULL );
1599 HDC hdcMem = CreateCompatibleDC( hdc );
1601 SelectObject( hdcMem, bm );
1602 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1603 DrawFrameControl( hdcMem, &r, DFC_MENU,
1604 (lpitem->fType & MFT_RADIOCHECK) ?
1605 DFCS_MENUBULLET : DFCS_MENUCHECK );
1606 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1607 hdcMem, 0, 0, SRCCOPY );
1613 if( lpitem->hbmpItem &&
1614 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1616 /* some applications make this assumption on the DC's origin */
1617 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1618 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1620 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1622 /* Draw the popup-menu arrow */
1623 if (lpitem->fType & MF_POPUP)
1624 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1625 arrow_bitmap_height);
1627 if( !(menu->dwStyle & MNS_NOCHECK))
1628 rect.left += check_bitmap_width;
1629 rect.right -= arrow_bitmap_width;
1631 else if( lpitem->hbmpItem)
1632 { /* Draw the bitmap */
1635 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1636 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1638 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1640 /* process text if present */
1646 UINT uFormat = (menuBar) ?
1647 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1648 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1650 if( !(menu->dwStyle & MNS_CHECKORBMP))
1651 rect.left += menu->maxBmpSize.cx;
1653 if ( lpitem->fState & MFS_DEFAULT )
1655 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1659 if( lpitem->hbmpItem)
1660 rect.left += lpitem->bmpsize.cx;
1661 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1662 rect.left += menucharsize.cx;
1663 rect.right -= menucharsize.cx;
1666 for (i = 0; lpitem->text[i]; i++)
1667 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1670 if(lpitem->fState & MF_GRAYED)
1672 if (!(lpitem->fState & MF_HILITE) )
1674 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1675 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1676 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1677 --rect.left; --rect.top; --rect.right; --rect.bottom;
1679 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1682 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1684 /* paint the shortcut text */
1685 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1687 if (lpitem->text[i] == '\t')
1689 rect.left = lpitem->xTab;
1690 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1694 rect.right = lpitem->xTab;
1695 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1698 if(lpitem->fState & MF_GRAYED)
1700 if (!(lpitem->fState & MF_HILITE) )
1702 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1703 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1704 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1705 --rect.left; --rect.top; --rect.right; --rect.bottom;
1707 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1709 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1713 SelectObject (hdc, hfontOld);
1718 /***********************************************************************
1719 * MENU_DrawPopupMenu
1721 * Paint a popup menu.
1723 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1725 HBRUSH hPrevBrush = 0;
1728 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1730 GetClientRect( hwnd, &rect );
1732 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1733 && (SelectObject( hdc, get_menu_font(FALSE))))
1737 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1739 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1743 BOOL flat_menu = FALSE;
1745 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1747 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1749 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1751 if( (menu = MENU_GetMenu( hmenu )))
1753 /* draw menu items */
1760 for( u = menu->nItems; u > 0; u--, item++)
1761 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1762 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1764 /* draw scroll arrows */
1765 if (menu->bScrolling)
1766 MENU_DrawScrollArrows(menu, hdc);
1770 SelectObject( hdc, hPrevBrush );
1775 /***********************************************************************
1778 * Paint a menu bar. Returns the height of the menu bar.
1779 * called from [windows/nonclient.c]
1781 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1786 HMENU hMenu = GetMenu(hwnd);
1788 lppop = MENU_GetMenu( hMenu );
1789 if (lppop == NULL || lprect == NULL)
1791 return GetSystemMetrics(SM_CYMENU);
1796 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1798 if (lppop->Height == 0)
1799 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1801 lprect->bottom = lprect->top + lppop->Height;
1803 if (hfontOld) SelectObject( hDC, hfontOld);
1804 return lppop->Height;
1807 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1811 /***********************************************************************
1814 * Display a popup menu.
1816 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1817 INT x, INT y, INT xanchor, INT yanchor )
1825 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1826 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1828 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1829 if (menu->FocusedItem != NO_SELECTED_ITEM)
1831 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1832 menu->FocusedItem = NO_SELECTED_ITEM;
1835 /* store the owner for DrawItem */
1836 menu->hwndOwner = hwndOwner;
1838 menu->nScrollPos = 0;
1839 MENU_PopupMenuCalcSize( menu );
1841 /* adjust popup menu pos so that it fits within the desktop */
1843 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1844 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1846 /* FIXME: should use item rect */
1849 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1850 info.cbSize = sizeof(info);
1851 GetMonitorInfoW( monitor, &info );
1853 if( flags & TPM_RIGHTALIGN ) x -= width;
1854 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1856 if( flags & TPM_BOTTOMALIGN ) y -= height;
1857 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1859 if( x + width > info.rcWork.right)
1861 if( xanchor && x >= width - xanchor )
1862 x -= width - xanchor;
1864 if( x + width > info.rcWork.right)
1865 x = info.rcWork.right - width;
1867 if( x < info.rcWork.left ) x = info.rcWork.left;
1869 if( y + height > info.rcWork.bottom)
1871 if( yanchor && y >= height + yanchor )
1872 y -= height + yanchor;
1874 if( y + height > info.rcWork.bottom)
1875 y = info.rcWork.bottom - height;
1877 if( y < info.rcWork.top ) y = info.rcWork.top;
1879 /* NOTE: In Windows, top menu popup is not owned. */
1880 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1881 WS_POPUP, x, y, width, height,
1882 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1884 if( !menu->hWnd ) return FALSE;
1885 if (!top_popup) top_popup = menu->hWnd;
1887 /* Display the window */
1889 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1890 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1891 UpdateWindow( menu->hWnd );
1896 /***********************************************************************
1897 * MENU_EnsureMenuItemVisible
1900 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1902 if (lppop->bScrolling)
1904 MENUITEM *item = &lppop->items[wIndex];
1905 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1906 UINT nOldPos = lppop->nScrollPos;
1908 UINT arrow_bitmap_height;
1911 GetClientRect(lppop->hWnd, &rc);
1913 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1914 arrow_bitmap_height = bmp.bmHeight;
1916 rc.top += arrow_bitmap_height;
1917 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1919 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1920 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1923 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1924 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1925 MENU_DrawScrollArrows(lppop, hdc);
1927 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1929 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1930 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1931 MENU_DrawScrollArrows(lppop, hdc);
1937 /***********************************************************************
1940 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1941 BOOL sendMenuSelect, HMENU topmenu )
1946 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1948 lppop = MENU_GetMenu( hmenu );
1949 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1951 if (lppop->FocusedItem == wIndex) return;
1952 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1953 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1954 if (!top_popup) top_popup = lppop->hWnd;
1956 SelectObject( hdc, get_menu_font(FALSE));
1958 /* Clear previous highlighted item */
1959 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1961 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1962 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1963 lppop->Height, !(lppop->wFlags & MF_POPUP),
1967 /* Highlight new item (if any) */
1968 lppop->FocusedItem = wIndex;
1969 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1971 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1972 lppop->items[wIndex].fState |= MF_HILITE;
1973 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1974 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1975 &lppop->items[wIndex], lppop->Height,
1976 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1980 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1981 SendMessageW( hwndOwner, WM_MENUSELECT,
1982 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1983 ip->fType | ip->fState |
1984 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1987 else if (sendMenuSelect) {
1990 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1991 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1992 MENUITEM *ip = &ptm->items[pos];
1993 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1994 ip->fType | ip->fState |
1995 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1999 ReleaseDC( lppop->hWnd, hdc );
2003 /***********************************************************************
2004 * MENU_MoveSelection
2006 * Moves currently selected item according to the offset parameter.
2007 * If there is no selection then it should select the last item if
2008 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2010 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2015 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2017 menu = MENU_GetMenu( hmenu );
2018 if ((!menu) || (!menu->items)) return;
2020 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2022 if( menu->nItems == 1 ) return; else
2023 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2025 if (!(menu->items[i].fType & MF_SEPARATOR))
2027 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2032 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2033 i >= 0 && i < menu->nItems ; i += offset)
2034 if (!(menu->items[i].fType & MF_SEPARATOR))
2036 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2042 /**********************************************************************
2045 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2048 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2051 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2052 TRACE("flags=%x str=%p\n", flags, str);
2054 if (IS_STRING_ITEM(flags))
2056 LPWSTR prevText = item->text;
2059 flags |= MF_SEPARATOR;
2065 /* Item beginning with a backspace is a help item */
2071 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2073 strcpyW( text, str );
2076 item->hbmpItem = NULL;
2077 HeapFree( GetProcessHeap(), 0, prevText );
2079 else if(( flags & MFT_BITMAP)) {
2080 item->hbmpItem = HBITMAP_32(LOWORD(str));
2081 /* setting bitmap clears text */
2082 HeapFree( GetProcessHeap(), 0, item->text );
2086 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2088 if (flags & MF_OWNERDRAW)
2089 item->dwItemData = (DWORD_PTR)str;
2091 item->dwItemData = 0;
2093 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2094 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2096 if (flags & MF_POPUP)
2098 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2099 if (menu) menu->wFlags |= MF_POPUP;
2111 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2113 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2114 flags |= MF_POPUP; /* keep popup */
2116 item->fType = flags & TYPE_MASK;
2117 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2118 for ModifyMenu, but Windows accepts it */
2119 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2121 /* Don't call SetRectEmpty here! */
2123 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2128 /**********************************************************************
2131 * Insert (allocate) a new item into a menu.
2133 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2138 if (!(menu = MENU_GetMenu(hMenu)))
2141 /* Find where to insert new item */
2143 if (flags & MF_BYPOSITION) {
2144 if (pos > menu->nItems)
2147 if (!MENU_FindItem( &hMenu, &pos, flags ))
2150 if (!(menu = MENU_GetMenu( hMenu )))
2155 /* Make sure that MDI system buttons stay on the right side.
2156 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2157 * regardless of their id.
2159 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2160 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2161 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2164 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2166 /* Create new items array */
2168 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2171 WARN("allocation failed\n" );
2174 if (menu->nItems > 0)
2176 /* Copy the old array into the new one */
2177 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2178 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2179 (menu->nItems-pos)*sizeof(MENUITEM) );
2180 HeapFree( GetProcessHeap(), 0, menu->items );
2182 menu->items = newItems;
2184 memset( &newItems[pos], 0, sizeof(*newItems) );
2185 menu->Height = 0; /* force size recalculate */
2186 return &newItems[pos];
2190 /**********************************************************************
2191 * MENU_ParseResource
2193 * Parse a standard menu resource and add items to the menu.
2194 * Return a pointer to the end of the resource.
2196 * NOTE: flags is equivalent to the mtOption field
2198 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2206 flags = GET_WORD(res);
2207 end_flag = flags & MF_END;
2208 /* Remove MF_END because it has the same value as MF_HILITE */
2210 res += sizeof(WORD);
2211 if (!(flags & MF_POPUP))
2214 res += sizeof(WORD);
2217 if (!unicode) res += strlen(str) + 1;
2218 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2219 if (flags & MF_POPUP)
2221 HMENU hSubMenu = CreatePopupMenu();
2222 if (!hSubMenu) return NULL;
2223 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2225 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2226 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2228 else /* Not a popup */
2230 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2231 else AppendMenuW( hMenu, flags, id,
2232 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2234 } while (!end_flag);
2239 /**********************************************************************
2240 * MENUEX_ParseResource
2242 * Parse an extended menu resource and add items to the menu.
2243 * Return a pointer to the end of the resource.
2245 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2251 mii.cbSize = sizeof(mii);
2252 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2253 mii.fType = GET_DWORD(res);
2254 res += sizeof(DWORD);
2255 mii.fState = GET_DWORD(res);
2256 res += sizeof(DWORD);
2257 mii.wID = GET_DWORD(res);
2258 res += sizeof(DWORD);
2259 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2260 res += sizeof(WORD);
2261 /* Align the text on a word boundary. */
2262 res += (~((UINT_PTR)res - 1)) & 1;
2263 mii.dwTypeData = (LPWSTR) res;
2264 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2265 /* Align the following fields on a dword boundary. */
2266 res += (~((UINT_PTR)res - 1)) & 3;
2268 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2269 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2271 if (resinfo & 1) { /* Pop-up? */
2272 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2273 res += sizeof(DWORD);
2274 mii.hSubMenu = CreatePopupMenu();
2277 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2278 DestroyMenu(mii.hSubMenu);
2281 mii.fMask |= MIIM_SUBMENU;
2282 mii.fType |= MF_POPUP;
2284 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2286 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2287 mii.wID, mii.fType);
2288 mii.fType |= MF_SEPARATOR;
2290 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2291 } while (!(resinfo & MF_END));
2296 /***********************************************************************
2299 * Return the handle of the selected sub-popup menu (if any).
2301 static HMENU MENU_GetSubPopup( HMENU hmenu )
2306 menu = MENU_GetMenu( hmenu );
2308 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2310 item = &menu->items[menu->FocusedItem];
2311 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2312 return item->hSubMenu;
2317 /***********************************************************************
2318 * MENU_HideSubPopups
2320 * Hide the sub-popup menus of this menu.
2322 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2323 BOOL sendMenuSelect, UINT wFlags )
2325 POPUPMENU *menu = MENU_GetMenu( hmenu );
2327 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2329 if (menu && top_popup)
2335 if (menu->FocusedItem != NO_SELECTED_ITEM)
2337 item = &menu->items[menu->FocusedItem];
2338 if (!(item->fType & MF_POPUP) ||
2339 !(item->fState & MF_MOUSESELECT)) return;
2340 item->fState &= ~MF_MOUSESELECT;
2341 hsubmenu = item->hSubMenu;
2344 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2345 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2346 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2347 DestroyWindow( submenu->hWnd );
2350 if (!(wFlags & TPM_NONOTIFY))
2351 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2352 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2357 /***********************************************************************
2360 * Display the sub-menu of the selected item of this menu.
2361 * Return the handle of the submenu, or hmenu if no submenu to display.
2363 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2364 BOOL selectFirst, UINT wFlags )
2371 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2373 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2375 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2377 item = &menu->items[menu->FocusedItem];
2378 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2381 /* message must be sent before using item,
2382 because nearly everything may be changed by the application ! */
2384 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2385 if (!(wFlags & TPM_NONOTIFY))
2386 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2387 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2389 item = &menu->items[menu->FocusedItem];
2392 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2393 if (!(item->fState & MF_HILITE))
2395 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2396 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2398 SelectObject( hdc, get_menu_font(FALSE));
2400 item->fState |= MF_HILITE;
2401 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2402 ReleaseDC( menu->hWnd, hdc );
2404 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2407 item->fState |= MF_MOUSESELECT;
2409 if (IS_SYSTEM_MENU(menu))
2411 MENU_InitSysMenuPopup(item->hSubMenu,
2412 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2413 GetClassLongW( menu->hWnd, GCL_STYLE));
2415 NC_GetSysPopupPos( menu->hWnd, &rect );
2416 rect.top = rect.bottom;
2417 rect.right = GetSystemMetrics(SM_CXSIZE);
2418 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2422 GetWindowRect( menu->hWnd, &rect );
2423 if (menu->wFlags & MF_POPUP)
2425 RECT rc = item->rect;
2427 MENU_AdjustMenuItemRect(menu, &rc);
2429 /* The first item in the popup menu has to be at the
2430 same y position as the focused menu item */
2431 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2432 rect.top += rc.top - MENU_TOP_MARGIN;
2433 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2434 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2435 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2439 rect.left += item->rect.left;
2440 rect.top += item->rect.bottom;
2441 rect.right = item->rect.right - item->rect.left;
2442 rect.bottom = item->rect.bottom - item->rect.top;
2446 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2447 rect.left, rect.top, rect.right, rect.bottom );
2449 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2450 return item->hSubMenu;
2455 /**********************************************************************
2458 HWND MENU_IsMenuActive(void)
2463 /***********************************************************************
2466 * Walks menu chain trying to find a menu pt maps to.
2468 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2470 POPUPMENU *menu = MENU_GetMenu( hMenu );
2471 UINT item = menu->FocusedItem;
2474 /* try subpopup first (if any) */
2475 ret = (item != NO_SELECTED_ITEM &&
2476 (menu->items[item].fType & MF_POPUP) &&
2477 (menu->items[item].fState & MF_MOUSESELECT))
2478 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2480 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2482 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2483 if( menu->wFlags & MF_POPUP )
2485 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2487 else if (ht == HTSYSMENU)
2488 ret = get_win_sys_menu( menu->hWnd );
2489 else if (ht == HTMENU)
2490 ret = GetMenu( menu->hWnd );
2495 /***********************************************************************
2496 * MENU_ExecFocusedItem
2498 * Execute a menu item (for instance when user pressed Enter).
2499 * Return the wID of the executed item. Otherwise, -1 indicating
2500 * that no menu item was executed, -2 if a popup is shown;
2501 * Have to receive the flags for the TrackPopupMenu options to avoid
2502 * sending unwanted message.
2505 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2508 POPUPMENU *menu = MENU_GetMenu( hMenu );
2510 TRACE("%p hmenu=%p\n", pmt, hMenu);
2512 if (!menu || !menu->nItems ||
2513 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2515 item = &menu->items[menu->FocusedItem];
2517 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2519 if (!(item->fType & MF_POPUP))
2521 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2523 /* If TPM_RETURNCMD is set you return the id, but
2524 do not send a message to the owner */
2525 if(!(wFlags & TPM_RETURNCMD))
2527 if( menu->wFlags & MF_SYSMENU )
2528 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2529 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2532 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2533 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2535 if (dwStyle & MNS_NOTIFYBYPOS)
2536 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2539 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2547 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2554 /***********************************************************************
2555 * MENU_SwitchTracking
2557 * Helper function for menu navigation routines.
2559 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2561 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2562 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2564 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2566 if( pmt->hTopMenu != hPtMenu &&
2567 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2569 /* both are top level menus (system and menu-bar) */
2570 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2571 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2572 pmt->hTopMenu = hPtMenu;
2574 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2575 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2579 /***********************************************************************
2582 * Return TRUE if we can go on with menu tracking.
2584 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2586 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2591 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2594 if( IS_SYSTEM_MENU(ptmenu) )
2595 item = ptmenu->items;
2597 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2601 if( ptmenu->FocusedItem != id )
2602 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2604 /* If the popup menu is not already "popped" */
2605 if(!(item->fState & MF_MOUSESELECT ))
2607 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2612 /* Else the click was on the menu bar, finish the tracking */
2617 /***********************************************************************
2620 * Return the value of MENU_ExecFocusedItem if
2621 * the selected item was not a popup. Else open the popup.
2622 * A -1 return value indicates that we go on with menu tracking.
2625 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2627 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2632 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2635 if( IS_SYSTEM_MENU(ptmenu) )
2636 item = ptmenu->items;
2638 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2640 if( item && (ptmenu->FocusedItem == id ))
2642 debug_print_menuitem ("FocusedItem: ", item, "");
2644 if( !(item->fType & MF_POPUP) )
2646 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2647 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2648 return executedMenuId;
2651 /* If we are dealing with the top-level menu */
2652 /* and this is a click on an already "popped" item: */
2653 /* Stop the menu tracking and close the opened submenus */
2654 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2657 ptmenu->bTimeToHide = TRUE;
2663 /***********************************************************************
2666 * Return TRUE if we can go on with menu tracking.
2668 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2670 UINT id = NO_SELECTED_ITEM;
2671 POPUPMENU *ptmenu = NULL;
2675 ptmenu = MENU_GetMenu( hPtMenu );
2676 if( IS_SYSTEM_MENU(ptmenu) )
2679 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2682 if( id == NO_SELECTED_ITEM )
2684 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2685 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2688 else if( ptmenu->FocusedItem != id )
2690 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2691 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2697 /***********************************************************************
2700 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2702 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2704 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2707 /* When skipping left, we need to do something special after the
2709 if (vk == VK_LEFT && menu->FocusedItem == 0)
2713 /* When skipping right, for the non-system menu, we need to
2714 handle the last non-special menu item (ie skip any window
2715 icons such as MDI maximize, restore or close) */
2716 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2718 UINT i = menu->FocusedItem + 1;
2719 while (i < menu->nItems) {
2720 if ((menu->items[i].wID >= SC_SIZE &&
2721 menu->items[i].wID <= SC_RESTORE)) {
2725 if (i == menu->nItems) {
2729 /* When skipping right, we need to cater for the system menu */
2730 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2732 if (menu->FocusedItem == (menu->nItems - 1)) {
2739 MDINEXTMENU next_menu;
2744 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2745 next_menu.hmenuNext = 0;
2746 next_menu.hwndNext = 0;
2747 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2749 TRACE("%p [%p] -> %p [%p]\n",
2750 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2752 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2754 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2755 hNewWnd = pmt->hOwnerWnd;
2756 if( IS_SYSTEM_MENU(menu) )
2758 /* switch to the menu bar */
2760 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2764 menu = MENU_GetMenu( hNewMenu );
2765 id = menu->nItems - 1;
2767 /* Skip backwards over any system predefined icons,
2768 eg. MDI close, restore etc icons */
2770 (menu->items[id].wID >= SC_SIZE &&
2771 menu->items[id].wID <= SC_RESTORE)) id--;
2774 else if (style & WS_SYSMENU )
2776 /* switch to the system menu */
2777 hNewMenu = get_win_sys_menu( hNewWnd );
2781 else /* application returned a new menu to switch to */
2783 hNewMenu = next_menu.hmenuNext;
2784 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2786 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2788 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2790 if (style & WS_SYSMENU &&
2791 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2793 /* get the real system menu */
2794 hNewMenu = get_win_sys_menu(hNewWnd);
2796 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2798 /* FIXME: Not sure what to do here;
2799 * perhaps try to track hNewMenu as a popup? */
2801 TRACE(" -- got confused.\n");
2808 if( hNewMenu != pmt->hTopMenu )
2810 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2812 if( pmt->hCurrentMenu != pmt->hTopMenu )
2813 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2816 if( hNewWnd != pmt->hOwnerWnd )
2818 pmt->hOwnerWnd = hNewWnd;
2819 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2822 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2823 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2830 /***********************************************************************
2833 * The idea is not to show the popup if the next input message is
2834 * going to hide it anyway.
2836 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2840 msg.hwnd = pmt->hOwnerWnd;
2842 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2843 pmt->trackFlags |= TF_SKIPREMOVE;
2848 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2849 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2851 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2852 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2853 if( msg.message == WM_KEYDOWN &&
2854 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2856 pmt->trackFlags |= TF_SUSPENDPOPUP;
2863 /* failures go through this */
2864 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2868 /***********************************************************************
2871 * Handle a VK_ESCAPE key event in a menu.
2873 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2875 BOOL bEndMenu = TRUE;
2877 if (pmt->hCurrentMenu != pmt->hTopMenu)
2879 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2881 if (menu->wFlags & MF_POPUP)
2883 HMENU hmenutmp, hmenuprev;
2885 hmenuprev = hmenutmp = pmt->hTopMenu;
2887 /* close topmost popup */
2888 while (hmenutmp != pmt->hCurrentMenu)
2890 hmenuprev = hmenutmp;
2891 hmenutmp = MENU_GetSubPopup( hmenuprev );
2894 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2895 pmt->hCurrentMenu = hmenuprev;
2903 /***********************************************************************
2906 * Handle a VK_LEFT key event in a menu.
2908 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2911 HMENU hmenutmp, hmenuprev;
2914 hmenuprev = hmenutmp = pmt->hTopMenu;
2915 menu = MENU_GetMenu( hmenutmp );
2917 /* Try to move 1 column left (if possible) */
2918 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2919 NO_SELECTED_ITEM ) {
2921 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2926 /* close topmost popup */
2927 while (hmenutmp != pmt->hCurrentMenu)
2929 hmenuprev = hmenutmp;
2930 hmenutmp = MENU_GetSubPopup( hmenuprev );
2933 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2934 pmt->hCurrentMenu = hmenuprev;
2936 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2938 /* move menu bar selection if no more popups are left */
2940 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2941 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2943 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2945 /* A sublevel menu was displayed - display the next one
2946 * unless there is another displacement coming up */
2948 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2950 pmt->hTopMenu, TRUE, wFlags);
2956 /***********************************************************************
2959 * Handle a VK_RIGHT key event in a menu.
2961 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2964 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2967 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2969 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2970 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2972 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2974 /* If already displaying a popup, try to display sub-popup */
2976 hmenutmp = pmt->hCurrentMenu;
2977 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2979 /* if subpopup was displayed then we are done */
2980 if (hmenutmp != pmt->hCurrentMenu) return;
2983 /* Check to see if there's another column */
2984 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2985 NO_SELECTED_ITEM ) {
2986 TRACE("Going to %d.\n", nextcol );
2987 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2992 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2994 if( pmt->hCurrentMenu != pmt->hTopMenu )
2996 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2997 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2998 } else hmenutmp = 0;
3000 /* try to move to the next item */
3001 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
3002 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
3004 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3005 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
3006 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3007 pmt->hTopMenu, TRUE, wFlags);
3011 static void CALLBACK release_capture( BOOL __normal )
3013 set_capture_window( 0, GUI_INMENUMODE, NULL );
3016 /***********************************************************************
3019 * Menu tracking code.
3021 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
3022 HWND hwnd, const RECT *lprect )
3027 INT executedMenuId = -1;
3029 BOOL enterIdleSent = FALSE;
3033 mt.hCurrentMenu = hmenu;
3034 mt.hTopMenu = hmenu;
3035 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3039 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3040 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3043 if (!(menu = MENU_GetMenu( hmenu )))
3045 WARN("Invalid menu handle %p\n", hmenu);
3046 SetLastError(ERROR_INVALID_MENU_HANDLE);
3050 if (wFlags & TPM_BUTTONDOWN)
3052 /* Get the result in order to start the tracking or not */
3053 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3054 fEndMenu = !fRemove;
3057 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3059 /* owner may not be visible when tracking a popup, so use the menu itself */
3060 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3061 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3063 __TRY while (!fEndMenu)
3065 menu = MENU_GetMenu( mt.hCurrentMenu );
3066 if (!menu) /* sometimes happens if I do a window manager close */
3069 /* we have to keep the message in the queue until it's
3070 * clear that menu loop is not over yet. */
3074 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3076 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3077 /* remove the message from the queue */
3078 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3084 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3085 enterIdleSent = TRUE;
3086 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3092 /* check if EndMenu() tried to cancel us, by posting this message */
3093 if(msg.message == WM_CANCELMODE)
3095 /* we are now out of the loop */
3098 /* remove the message from the queue */
3099 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3101 /* break out of internal loop, ala ESCAPE */
3105 TranslateMessage( &msg );
3108 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3109 enterIdleSent=FALSE;
3112 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3115 * Use the mouse coordinates in lParam instead of those in the MSG
3116 * struct to properly handle synthetic messages. They are already
3117 * in screen coordinates.
3119 mt.pt.x = (short)LOWORD(msg.lParam);
3120 mt.pt.y = (short)HIWORD(msg.lParam);
3122 /* Find a menu for this mouse event */
3123 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3127 /* no WM_NC... messages in captured state */
3129 case WM_RBUTTONDBLCLK:
3130 case WM_RBUTTONDOWN:
3131 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3133 case WM_LBUTTONDBLCLK:
3134 case WM_LBUTTONDOWN:
3135 /* If the message belongs to the menu, removes it from the queue */
3136 /* Else, end menu tracking */
3137 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3138 fEndMenu = !fRemove;
3142 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3145 /* Check if a menu was selected by the mouse */
3148 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3149 TRACE("executedMenuId %d\n", executedMenuId);
3151 /* End the loop if executedMenuId is an item ID */
3152 /* or if the job was done (executedMenuId = 0). */
3153 fEndMenu = fRemove = (executedMenuId != -1);
3155 /* No menu was selected by the mouse */
3156 /* if the function was called by TrackPopupMenu, continue
3157 with the menu tracking. If not, stop it */
3159 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3164 /* the selected menu item must be changed every time */
3165 /* the mouse moves. */
3168 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3170 } /* switch(msg.message) - mouse */
3172 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3174 fRemove = TRUE; /* Keyboard messages are always removed */
3187 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3188 NO_SELECTED_ITEM, FALSE, 0 );
3189 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3190 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3194 case VK_DOWN: /* If on menu bar, pull-down the menu */
3196 menu = MENU_GetMenu( mt.hCurrentMenu );
3197 if (!(menu->wFlags & MF_POPUP))
3198 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3199 else /* otherwise try to move selection */
3200 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3201 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3205 MENU_KeyLeft( &mt, wFlags );
3209 MENU_KeyRight( &mt, wFlags );
3213 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3219 hi.cbSize = sizeof(HELPINFO);
3220 hi.iContextType = HELPINFO_MENUITEM;
3221 if (menu->FocusedItem == NO_SELECTED_ITEM)
3224 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3225 hi.hItemHandle = hmenu;
3226 hi.dwContextId = menu->dwContextHelpID;
3227 hi.MousePos = msg.pt;
3228 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3235 break; /* WM_KEYDOWN */
3242 if (msg.wParam == '\r' || msg.wParam == ' ')
3244 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3245 fEndMenu = (executedMenuId != -2);
3250 /* Hack to avoid control chars. */
3251 /* We will find a better way real soon... */
3252 if (msg.wParam < 32) break;
3254 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3255 LOWORD(msg.wParam), FALSE );
3256 if (pos == (UINT)-2) fEndMenu = TRUE;
3257 else if (pos == (UINT)-1) MessageBeep(0);
3260 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3262 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3263 fEndMenu = (executedMenuId != -2);
3267 } /* switch(msg.message) - kbd */
3271 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3272 DispatchMessageW( &msg );
3276 if (!fEndMenu) fRemove = TRUE;
3278 /* finally remove message from the queue */
3280 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3281 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3282 else mt.trackFlags &= ~TF_SKIPREMOVE;
3284 __FINALLY( release_capture )
3286 /* If dropdown is still painted and the close box is clicked on
3287 then the menu will be destroyed as part of the DispatchMessage above.
3288 This will then invalidate the menu handle in mt.hTopMenu. We should
3289 check for this first. */
3290 if( IsMenu( mt.hTopMenu ) )
3292 menu = MENU_GetMenu( mt.hTopMenu );
3294 if( IsWindow( mt.hOwnerWnd ) )
3296 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3298 if (menu && (menu->wFlags & MF_POPUP))
3300 DestroyWindow( menu->hWnd );
3303 if (!(wFlags & TPM_NONOTIFY))
3304 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3305 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3307 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3308 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3311 /* Reset the variable for hiding menu */
3312 if( menu ) menu->bTimeToHide = FALSE;
3315 /* The return value is only used by TrackPopupMenu */
3316 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3317 if (executedMenuId == -1) executedMenuId = 0;
3318 return executedMenuId;
3321 /***********************************************************************
3324 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3328 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3332 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3333 if (!(wFlags & TPM_NONOTIFY))
3334 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3336 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3338 if (!(wFlags & TPM_NONOTIFY))
3340 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3341 /* If an app changed/recreated menu bar entries in WM_INITMENU
3342 * menu sizes will be recalculated once the menu created/shown.
3346 /* This makes the menus of applications built with Delphi work.
3347 * It also enables menus to be displayed in more than one window,
3348 * but there are some bugs left that need to be fixed in this case.
3350 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3354 /***********************************************************************
3357 static BOOL MENU_ExitTracking(HWND hWnd)
3359 TRACE("hwnd=%p\n", hWnd);
3361 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3367 /***********************************************************************
3368 * MENU_TrackMouseMenuBar
3370 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3372 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3374 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3375 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3377 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3381 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3382 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3383 MENU_ExitTracking(hWnd);
3388 /***********************************************************************
3389 * MENU_TrackKbdMenuBar
3391 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3393 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3395 UINT uItem = NO_SELECTED_ITEM;
3397 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3399 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3401 /* find window that has a menu */
3403 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3404 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3406 /* check if we have to track a system menu */
3408 hTrackMenu = GetMenu( hwnd );
3409 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3411 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3412 hTrackMenu = get_win_sys_menu( hwnd );
3414 wParam |= HTSYSMENU; /* prevent item lookup */
3417 if (!IsMenu( hTrackMenu )) return;
3419 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3421 if( wChar && wChar != ' ' )
3423 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3424 if ( uItem >= (UINT)(-2) )
3426 if( uItem == (UINT)(-1) ) MessageBeep(0);
3427 /* schedule end of menu tracking */
3428 wFlags |= TF_ENDMENU;
3433 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3435 if (!(wParam & HTSYSMENU) || wChar == ' ')
3437 if( uItem == NO_SELECTED_ITEM )
3438 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3440 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3444 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3445 MENU_ExitTracking( hwnd );
3449 /**********************************************************************
3450 * TrackPopupMenu (USER32.@)
3452 * Like the win32 API, the function return the command ID only if the
3453 * flag TPM_RETURNCMD is on.
3456 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3457 INT nReserved, HWND hWnd, const RECT *lpRect )
3461 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3462 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3464 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3466 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3467 if (!(wFlags & TPM_NONOTIFY))
3468 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3470 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3471 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3472 MENU_ExitTracking(hWnd);
3477 /**********************************************************************
3478 * TrackPopupMenuEx (USER32.@)
3480 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3481 HWND hWnd, LPTPMPARAMS lpTpm )
3483 FIXME("not fully implemented\n" );
3484 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3485 lpTpm ? &lpTpm->rcExclude : NULL );
3488 /***********************************************************************
3491 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3493 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3495 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3501 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3502 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3506 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3507 return MA_NOACTIVATE;
3512 BeginPaint( hwnd, &ps );
3513 MENU_DrawPopupMenu( hwnd, ps.hdc,
3514 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3515 EndPaint( hwnd, &ps );
3522 /* zero out global pointer in case resident popup window was destroyed. */
3523 if (hwnd == top_popup) top_popup = 0;
3530 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3533 SetWindowLongPtrW( hwnd, 0, 0 );
3536 case MM_SETMENUHANDLE:
3537 SetWindowLongPtrW( hwnd, 0, wParam );
3540 case MM_GETMENUHANDLE:
3541 return GetWindowLongPtrW( hwnd, 0 );
3544 return DefWindowProcW( hwnd, message, wParam, lParam );
3550 /***********************************************************************
3551 * MENU_GetMenuBarHeight
3553 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3555 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3556 INT orgX, INT orgY )
3562 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3564 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3566 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3567 SelectObject( hdc, get_menu_font(FALSE));
3568 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3569 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3570 ReleaseDC( hwnd, hdc );
3571 return lppop->Height;
3575 /*******************************************************************
3576 * ChangeMenuA (USER32.@)
3578 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3579 UINT id, UINT flags )
3581 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3582 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3584 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3585 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3587 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3588 flags & MF_BYPOSITION ? pos : id,
3589 flags & ~MF_REMOVE );
3590 /* Default: MF_INSERT */
3591 return InsertMenuA( hMenu, pos, flags, id, data );
3595 /*******************************************************************
3596 * ChangeMenuW (USER32.@)
3598 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3599 UINT id, UINT flags )
3601 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3602 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3604 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3605 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3607 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3608 flags & MF_BYPOSITION ? pos : id,
3609 flags & ~MF_REMOVE );
3610 /* Default: MF_INSERT */
3611 return InsertMenuW( hMenu, pos, flags, id, data );
3615 /*******************************************************************
3616 * CheckMenuItem (USER32.@)
3618 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3623 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3624 ret = item->fState & MF_CHECKED;
3625 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3626 else item->fState &= ~MF_CHECKED;
3631 /**********************************************************************
3632 * EnableMenuItem (USER32.@)
3634 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3640 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3642 /* Get the Popupmenu to access the owner menu */
3643 if (!(menu = MENU_GetMenu(hMenu)))
3646 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3649 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3650 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3652 /* If the close item in the system menu change update the close button */
3653 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3655 if (menu->hSysMenuOwner != 0)
3658 POPUPMENU* parentMenu;
3660 /* Get the parent menu to access*/
3661 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3664 /* Refresh the frame to reflect the change */
3665 GetWindowRect(parentMenu->hWnd, &rc);
3666 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3668 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3676 /*******************************************************************
3677 * GetMenuStringA (USER32.@)
3679 INT WINAPI GetMenuStringA(
3680 HMENU hMenu, /* [in] menuhandle */
3681 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3682 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3683 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3684 UINT wFlags /* [in] MF_ flags */
3688 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3689 if (str && nMaxSiz) str[0] = '\0';
3690 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3691 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3694 if (!item->text) return 0;
3695 if (!str || !nMaxSiz) return strlenW(item->text);
3696 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3698 TRACE("returning %s\n", debugstr_a(str));
3703 /*******************************************************************
3704 * GetMenuStringW (USER32.@)
3706 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3707 LPWSTR str, INT nMaxSiz, UINT wFlags )
3711 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3712 if (str && nMaxSiz) str[0] = '\0';
3713 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3714 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3717 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3718 if( !(item->text)) {
3722 lstrcpynW( str, item->text, nMaxSiz );
3723 TRACE("returning %s\n", debugstr_w(str));
3724 return strlenW(str);
3728 /**********************************************************************
3729 * HiliteMenuItem (USER32.@)
3731 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3735 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3736 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3737 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3738 if (menu->FocusedItem == wItemID) return TRUE;
3739 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3740 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3745 /**********************************************************************
3746 * GetMenuState (USER32.@)
3748 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3751 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3752 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3753 debug_print_menuitem (" item: ", item, "");
3754 if (item->fType & MF_POPUP)
3756 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3757 if (!menu) return -1;
3758 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3762 /* We used to (from way back then) mask the result to 0xff. */
3763 /* I don't know why and it seems wrong as the documented */
3764 /* return flag MF_SEPARATOR is outside that mask. */
3765 return (item->fType | item->fState);
3770 /**********************************************************************
3771 * GetMenuItemCount (USER32.@)
3773 INT WINAPI GetMenuItemCount( HMENU hMenu )
3775 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3776 if (!menu) return -1;
3777 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3778 return menu->nItems;
3782 /**********************************************************************
3783 * GetMenuItemID (USER32.@)
3785 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3789 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3790 if (lpmi->fType & MF_POPUP) return -1;
3796 /*******************************************************************
3797 * InsertMenuW (USER32.@)
3799 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3800 UINT_PTR id, LPCWSTR str )
3804 if (IS_STRING_ITEM(flags) && str)
3805 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3806 hMenu, pos, flags, id, debugstr_w(str) );
3807 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3808 hMenu, pos, flags, id, str );
3810 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3812 if (!(MENU_SetItemData( item, flags, id, str )))
3814 RemoveMenu( hMenu, pos, flags );
3818 item->hCheckBit = item->hUnCheckBit = 0;
3823 /*******************************************************************
3824 * InsertMenuA (USER32.@)
3826 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3827 UINT_PTR id, LPCSTR str )
3831 if (IS_STRING_ITEM(flags) && str)
3833 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3834 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3837 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3838 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3839 HeapFree( GetProcessHeap(), 0, newstr );
3843 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3847 /*******************************************************************
3848 * AppendMenuA (USER32.@)
3850 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3851 UINT_PTR id, LPCSTR data )
3853 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3857 /*******************************************************************
3858 * AppendMenuW (USER32.@)
3860 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3861 UINT_PTR id, LPCWSTR data )
3863 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3867 /**********************************************************************
3868 * RemoveMenu (USER32.@)
3870 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3875 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3876 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3877 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3881 MENU_FreeItemData( item );
3883 if (--menu->nItems == 0)
3885 HeapFree( GetProcessHeap(), 0, menu->items );
3890 while(nPos < menu->nItems)
3896 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3897 menu->nItems * sizeof(MENUITEM) );
3903 /**********************************************************************
3904 * DeleteMenu (USER32.@)
3906 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3908 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3909 if (!item) return FALSE;
3910 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3911 /* nPos is now the position of the item */
3912 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3917 /*******************************************************************
3918 * ModifyMenuW (USER32.@)
3920 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3921 UINT_PTR id, LPCWSTR str )
3925 if (IS_STRING_ITEM(flags))
3926 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3928 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3930 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3931 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3932 return MENU_SetItemData( item, flags, id, str );
3936 /*******************************************************************
3937 * ModifyMenuA (USER32.@)
3939 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3940 UINT_PTR id, LPCSTR str )
3944 if (IS_STRING_ITEM(flags) && str)
3946 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3947 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3950 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3951 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3952 HeapFree( GetProcessHeap(), 0, newstr );
3956 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3960 /**********************************************************************
3961 * CreatePopupMenu (USER32.@)
3963 HMENU WINAPI CreatePopupMenu(void)
3968 if (!(hmenu = CreateMenu())) return 0;
3969 menu = MENU_GetMenu( hmenu );
3970 menu->wFlags |= MF_POPUP;
3971 menu->bTimeToHide = FALSE;
3976 /**********************************************************************
3977 * GetMenuCheckMarkDimensions (USER.417)
3978 * GetMenuCheckMarkDimensions (USER32.@)
3980 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3982 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3986 /**********************************************************************
3987 * SetMenuItemBitmaps (USER32.@)
3989 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3990 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3994 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3996 if (!hNewCheck && !hNewUnCheck)
3998 item->fState &= ~MF_USECHECKBITMAPS;
4000 else /* Install new bitmaps */
4002 item->hCheckBit = hNewCheck;
4003 item->hUnCheckBit = hNewUnCheck;
4004 item->fState |= MF_USECHECKBITMAPS;
4010 /**********************************************************************
4011 * CreateMenu (USER32.@)
4013 HMENU WINAPI CreateMenu(void)
4017 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4018 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
4020 ZeroMemory(menu, sizeof(POPUPMENU));
4021 menu->wMagic = MENU_MAGIC;
4022 menu->FocusedItem = NO_SELECTED_ITEM;
4023 menu->bTimeToHide = FALSE;
4025 TRACE("return %p\n", hMenu );
4031 /**********************************************************************
4032 * DestroyMenu (USER32.@)
4034 BOOL WINAPI DestroyMenu( HMENU hMenu )
4036 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4038 TRACE("(%p)\n", hMenu);
4041 if (!lppop) return FALSE;
4043 lppop->wMagic = 0; /* Mark it as destroyed */
4045 /* DestroyMenu should not destroy system menu popup owner */
4046 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4048 DestroyWindow( lppop->hWnd );
4052 if (lppop->items) /* recursively destroy submenus */
4055 MENUITEM *item = lppop->items;
4056 for (i = lppop->nItems; i > 0; i--, item++)
4058 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4059 MENU_FreeItemData( item );
4061 HeapFree( GetProcessHeap(), 0, lppop->items );
4063 USER_HEAP_FREE( hMenu );
4068 /**********************************************************************
4069 * GetSystemMenu (USER32.@)
4071 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4073 WND *wndPtr = WIN_GetPtr( hWnd );
4076 if (wndPtr == WND_DESKTOP) return 0;
4077 if (wndPtr == WND_OTHER_PROCESS)
4079 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4083 if (wndPtr->hSysMenu && bRevert)
4085 DestroyMenu(wndPtr->hSysMenu);
4086 wndPtr->hSysMenu = 0;
4089 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4090 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4092 if( wndPtr->hSysMenu )
4095 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4097 /* Store the dummy sysmenu handle to facilitate the refresh */
4098 /* of the close button if the SC_CLOSE item change */
4099 menu = MENU_GetMenu(retvalue);
4101 menu->hSysMenuOwner = wndPtr->hSysMenu;
4103 WIN_ReleasePtr( wndPtr );
4105 return bRevert ? 0 : retvalue;
4109 /*******************************************************************
4110 * SetSystemMenu (USER32.@)
4112 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4114 WND *wndPtr = WIN_GetPtr( hwnd );
4116 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4118 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4119 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4120 WIN_ReleasePtr( wndPtr );
4127 /**********************************************************************
4128 * GetMenu (USER32.@)
4130 HMENU WINAPI GetMenu( HWND hWnd )
4132 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4133 TRACE("for %p returning %p\n", hWnd, retvalue);
4137 /**********************************************************************
4138 * GetMenuBarInfo (USER32.@)
4140 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4142 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4146 /**********************************************************************
4149 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4150 * SetWindowPos call that would result if SetMenu were called directly.
4152 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4154 TRACE("(%p, %p);\n", hWnd, hMenu);
4156 if (hMenu && !IsMenu(hMenu))
4158 WARN("hMenu %p is not a menu handle\n", hMenu);
4161 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4164 hWnd = WIN_GetFullHandle( hWnd );
4165 if (GetCapture() == hWnd)
4166 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4172 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4174 lpmenu->hWnd = hWnd;
4175 lpmenu->Height = 0; /* Make sure we recalculate the size */
4177 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4182 /**********************************************************************
4183 * SetMenu (USER32.@)
4185 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4187 if(!MENU_SetMenu(hWnd, hMenu))
4190 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4191 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4196 /**********************************************************************
4197 * GetSubMenu (USER32.@)
4199 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4203 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4204 if (!(lpmi->fType & MF_POPUP)) return 0;
4205 return lpmi->hSubMenu;
4209 /**********************************************************************
4210 * DrawMenuBar (USER32.@)
4212 BOOL WINAPI DrawMenuBar( HWND hWnd )
4215 HMENU hMenu = GetMenu(hWnd);
4217 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4219 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4221 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4222 lppop->hwndOwner = hWnd;
4223 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4224 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4228 /***********************************************************************
4229 * DrawMenuBarTemp (USER32.@)
4233 * called by W98SE desk.cpl Control Panel Applet
4235 * Not 100% sure about the param names, but close.
4237 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4242 BOOL flat_menu = FALSE;
4244 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4247 hMenu = GetMenu(hwnd);
4250 hFont = get_menu_font(FALSE);
4252 lppop = MENU_GetMenu( hMenu );
4253 if (lppop == NULL || lprect == NULL)
4255 retvalue = GetSystemMetrics(SM_CYMENU);
4259 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4261 hfontOld = SelectObject( hDC, hFont);
4263 if (lppop->Height == 0)
4264 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4266 lprect->bottom = lprect->top + lppop->Height;
4268 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4270 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4271 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4272 LineTo( hDC, lprect->right, lprect->bottom );
4274 if (lppop->nItems == 0)
4276 retvalue = GetSystemMetrics(SM_CYMENU);
4280 for (i = 0; i < lppop->nItems; i++)
4282 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4283 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4285 retvalue = lppop->Height;
4288 if (hfontOld) SelectObject (hDC, hfontOld);
4292 /***********************************************************************
4293 * EndMenu (USER.187)
4294 * EndMenu (USER32.@)
4296 BOOL WINAPI EndMenu(void)
4298 /* if we are in the menu code, and it is active */
4299 if (!fEndMenu && top_popup)
4301 /* terminate the menu handling code */
4304 /* needs to be posted to wakeup the internal menu handler */
4305 /* which will now terminate the menu, in the event that */
4306 /* the main window was minimized, or lost focus, so we */
4307 /* don't end up with an orphaned menu */
4308 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4314 /***********************************************************************
4315 * LookupMenuHandle (USER.217)
4317 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4319 HMENU hmenu32 = HMENU_32(hmenu);
4321 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4322 else return HMENU_16(hmenu32);
4326 /**********************************************************************
4327 * LoadMenu (USER.150)
4329 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4335 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4336 if (!name) return 0;
4338 instance = GetExePtr( instance );
4339 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4340 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4341 hMenu = LoadMenuIndirect16(LockResource16(handle));
4342 FreeResource16( handle );
4347 /*****************************************************************
4348 * LoadMenuA (USER32.@)
4350 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4352 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4353 if (!hrsrc) return 0;
4354 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4358 /*****************************************************************
4359 * LoadMenuW (USER32.@)
4361 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4363 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4364 if (!hrsrc) return 0;
4365 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4369 /**********************************************************************
4370 * LoadMenuIndirect (USER.220)
4372 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4375 WORD version, offset;
4376 LPCSTR p = (LPCSTR)template;
4378 TRACE("(%p)\n", template );
4379 version = GET_WORD(p);
4383 WARN("version must be 0 for Win16\n" );
4386 offset = GET_WORD(p);
4387 p += sizeof(WORD) + offset;
4388 if (!(hMenu = CreateMenu())) return 0;
4389 if (!MENU_ParseResource( p, hMenu, FALSE ))
4391 DestroyMenu( hMenu );
4394 return HMENU_16(hMenu);
4398 /**********************************************************************
4399 * LoadMenuIndirectW (USER32.@)
4401 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4404 WORD version, offset;
4405 LPCSTR p = (LPCSTR)template;
4407 version = GET_WORD(p);
4409 TRACE("%p, ver %d\n", template, version );
4412 case 0: /* standard format is version of 0 */
4413 offset = GET_WORD(p);
4414 p += sizeof(WORD) + offset;
4415 if (!(hMenu = CreateMenu())) return 0;
4416 if (!MENU_ParseResource( p, hMenu, TRUE ))
4418 DestroyMenu( hMenu );
4422 case 1: /* extended format is version of 1 */
4423 offset = GET_WORD(p);
4424 p += sizeof(WORD) + offset;
4425 if (!(hMenu = CreateMenu())) return 0;
4426 if (!MENUEX_ParseResource( p, hMenu))
4428 DestroyMenu( hMenu );
4433 ERR("version %d not supported.\n", version);
4439 /**********************************************************************
4440 * LoadMenuIndirectA (USER32.@)
4442 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4444 return LoadMenuIndirectW( template );
4448 /**********************************************************************
4451 BOOL WINAPI IsMenu(HMENU hmenu)
4453 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4457 SetLastError(ERROR_INVALID_MENU_HANDLE);
4463 /**********************************************************************
4464 * GetMenuItemInfo_common
4467 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4468 LPMENUITEMINFOW lpmii, BOOL unicode)
4470 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4472 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4475 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4479 if( lpmii->fMask & MIIM_TYPE) {
4480 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4481 WARN("invalid combination of fMask bits used\n");
4482 /* this does not happen on Win9x/ME */
4483 SetLastError( ERROR_INVALID_PARAMETER);
4486 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4487 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4488 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4489 if( lpmii->fType & MFT_BITMAP) {
4490 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4492 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4493 /* this does not happen on Win9x/ME */
4494 lpmii->dwTypeData = 0;
4499 /* copy the text string */
4500 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4502 if(lpmii->dwTypeData && lpmii->cch) {
4505 *((WCHAR *)lpmii->dwTypeData) = 0;
4507 *((CHAR *)lpmii->dwTypeData) = 0;
4513 len = strlenW(menu->text);
4514 if(lpmii->dwTypeData && lpmii->cch)
4515 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4519 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4520 0, NULL, NULL ) - 1;
4521 if(lpmii->dwTypeData && lpmii->cch)
4522 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4523 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4524 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4526 /* if we've copied a substring we return its length */
4527 if(lpmii->dwTypeData && lpmii->cch)
4528 if (lpmii->cch <= len + 1)
4533 /* return length of string */
4534 /* not on Win9x/ME if fType & MFT_BITMAP */
4540 if (lpmii->fMask & MIIM_FTYPE)
4541 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4543 if (lpmii->fMask & MIIM_BITMAP)
4544 lpmii->hbmpItem = menu->hbmpItem;
4546 if (lpmii->fMask & MIIM_STATE)
4547 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4549 if (lpmii->fMask & MIIM_ID)
4550 lpmii->wID = menu->wID;
4552 if (lpmii->fMask & MIIM_SUBMENU)
4553 lpmii->hSubMenu = menu->hSubMenu;
4555 /* hSubMenu is always cleared
4556 * (not on Win9x/ME ) */
4557 lpmii->hSubMenu = 0;
4560 if (lpmii->fMask & MIIM_CHECKMARKS) {
4561 lpmii->hbmpChecked = menu->hCheckBit;
4562 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4564 if (lpmii->fMask & MIIM_DATA)
4565 lpmii->dwItemData = menu->dwItemData;
4570 /**********************************************************************
4571 * GetMenuItemInfoA (USER32.@)
4573 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4574 LPMENUITEMINFOA lpmii)
4578 if( lpmii->cbSize != sizeof( mii) &&
4579 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4580 SetLastError( ERROR_INVALID_PARAMETER);
4583 memcpy( &mii, lpmii, lpmii->cbSize);
4584 mii.cbSize = sizeof( mii);
4585 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4586 (LPMENUITEMINFOW)&mii, FALSE);
4587 mii.cbSize = lpmii->cbSize;
4588 memcpy( lpmii, &mii, mii.cbSize);
4592 /**********************************************************************
4593 * GetMenuItemInfoW (USER32.@)
4595 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4596 LPMENUITEMINFOW lpmii)
4600 if( lpmii->cbSize != sizeof( mii) &&
4601 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4602 SetLastError( ERROR_INVALID_PARAMETER);
4605 memcpy( &mii, lpmii, lpmii->cbSize);
4606 mii.cbSize = sizeof( mii);
4607 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4608 mii.cbSize = lpmii->cbSize;
4609 memcpy( lpmii, &mii, mii.cbSize);
4614 /* set a menu item text from a ASCII or Unicode string */
4615 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4621 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4622 strcpyW( menu->text, text );
4626 LPCSTR str = (LPCSTR)text;
4627 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4628 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4629 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4634 /**********************************************************************
4635 * SetMenuItemInfo_common
4638 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4639 const MENUITEMINFOW *lpmii,
4642 if (!menu) return FALSE;
4644 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4646 if (lpmii->fMask & MIIM_TYPE ) {
4647 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4648 WARN("invalid combination of fMask bits used\n");
4649 /* this does not happen on Win9x/ME */
4650 SetLastError( ERROR_INVALID_PARAMETER);
4654 /* Remove the old type bits and replace them with the new ones */
4655 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4656 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4658 if (IS_STRING_ITEM(menu->fType)) {
4659 HeapFree(GetProcessHeap(), 0, menu->text);
4660 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4661 } else if( (menu->fType) & MFT_BITMAP)
4662 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4665 if (lpmii->fMask & MIIM_FTYPE ) {
4666 if(( lpmii->fType & MFT_BITMAP)) {
4667 SetLastError( ERROR_INVALID_PARAMETER);
4670 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4671 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4673 if (lpmii->fMask & MIIM_STRING ) {
4674 /* free the string when used */
4675 HeapFree(GetProcessHeap(), 0, menu->text);
4676 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4679 if (lpmii->fMask & MIIM_STATE)
4681 /* Other menu items having MFS_DEFAULT are not converted
4683 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4686 if (lpmii->fMask & MIIM_ID)
4687 menu->wID = lpmii->wID;
4689 if (lpmii->fMask & MIIM_SUBMENU) {
4690 menu->hSubMenu = lpmii->hSubMenu;
4691 if (menu->hSubMenu) {
4692 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4694 subMenu->wFlags |= MF_POPUP;
4695 menu->fType |= MF_POPUP;
4698 SetLastError( ERROR_INVALID_PARAMETER);
4703 menu->fType &= ~MF_POPUP;
4706 if (lpmii->fMask & MIIM_CHECKMARKS)
4708 menu->hCheckBit = lpmii->hbmpChecked;
4709 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4711 if (lpmii->fMask & MIIM_DATA)
4712 menu->dwItemData = lpmii->dwItemData;
4714 if (lpmii->fMask & MIIM_BITMAP)
4715 menu->hbmpItem = lpmii->hbmpItem;
4717 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4718 menu->fType |= MFT_SEPARATOR;
4720 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4724 /**********************************************************************
4725 * SetMenuItemInfoA (USER32.@)
4727 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4728 const MENUITEMINFOA *lpmii)
4732 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4734 if( lpmii->cbSize != sizeof( mii) &&
4735 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4736 SetLastError( ERROR_INVALID_PARAMETER);
4739 memcpy( &mii, lpmii, lpmii->cbSize);
4740 if( lpmii->cbSize != sizeof( mii)) {
4741 mii.cbSize = sizeof( mii);
4742 mii.hbmpItem = NULL;
4744 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4745 (const MENUITEMINFOW *)&mii, FALSE);
4748 /**********************************************************************
4749 * SetMenuItemInfoW (USER32.@)
4751 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4752 const MENUITEMINFOW *lpmii)
4756 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4758 if( lpmii->cbSize != sizeof( mii) &&
4759 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4760 SetLastError( ERROR_INVALID_PARAMETER);
4763 memcpy( &mii, lpmii, lpmii->cbSize);
4764 if( lpmii->cbSize != sizeof( mii)) {
4765 mii.cbSize = sizeof( mii);
4766 mii.hbmpItem = NULL;
4768 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4769 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4772 /**********************************************************************
4773 * SetMenuDefaultItem (USER32.@)
4776 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4782 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4784 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4786 /* reset all default-item flags */
4788 for (i = 0; i < menu->nItems; i++, item++)
4790 item->fState &= ~MFS_DEFAULT;
4793 /* no default item */
4802 if ( uItem >= menu->nItems ) return FALSE;
4803 item[uItem].fState |= MFS_DEFAULT;
4808 for (i = 0; i < menu->nItems; i++, item++)
4810 if (item->wID == uItem)
4812 item->fState |= MFS_DEFAULT;
4821 /**********************************************************************
4822 * GetMenuDefaultItem (USER32.@)
4824 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4830 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4832 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4834 /* find default item */
4838 if (! item) return -1;
4840 while ( !( item->fState & MFS_DEFAULT ) )
4843 if (i >= menu->nItems ) return -1;
4846 /* default: don't return disabled items */
4847 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4849 /* search rekursiv when needed */
4850 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4853 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4854 if ( -1 != ret ) return ret;
4856 /* when item not found in submenu, return the popup item */
4858 return ( bypos ) ? i : item->wID;
4863 /**********************************************************************
4864 * InsertMenuItemA (USER32.@)
4866 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4867 const MENUITEMINFOA *lpmii)
4872 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4874 if( lpmii->cbSize != sizeof( mii) &&
4875 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4876 SetLastError( ERROR_INVALID_PARAMETER);
4879 memcpy( &mii, lpmii, lpmii->cbSize);
4880 if( lpmii->cbSize != sizeof( mii)) {
4881 mii.cbSize = sizeof( mii);
4882 mii.hbmpItem = NULL;
4885 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4886 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4890 /**********************************************************************
4891 * InsertMenuItemW (USER32.@)
4893 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4894 const MENUITEMINFOW *lpmii)
4899 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4901 if( lpmii->cbSize != sizeof( mii) &&
4902 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4903 SetLastError( ERROR_INVALID_PARAMETER);
4906 memcpy( &mii, lpmii, lpmii->cbSize);
4907 if( lpmii->cbSize != sizeof( mii)) {
4908 mii.cbSize = sizeof( mii);
4909 mii.hbmpItem = NULL;
4912 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4913 return SetMenuItemInfo_common(item, &mii, TRUE);
4916 /**********************************************************************
4917 * CheckMenuRadioItem (USER32.@)
4920 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4921 UINT first, UINT last, UINT check,
4926 MENUITEM *mi_first = NULL, *mi_check;
4927 HMENU m_first, m_check;
4929 for (i = first; i <= last; i++)
4936 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4937 if (!mi_first) continue;
4938 mi_check = mi_first;
4944 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4945 if (!mi_check) continue;
4948 if (m_first != m_check) continue;
4949 if (mi_check->fType == MFT_SEPARATOR) continue;
4953 mi_check->fType |= MFT_RADIOCHECK;
4954 mi_check->fState |= MFS_CHECKED;
4959 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4960 mi_check->fState &= ~MFS_CHECKED;
4968 /**********************************************************************
4969 * GetMenuItemRect (USER32.@)
4971 * ATTENTION: Here, the returned values in rect are the screen
4972 * coordinates of the item just like if the menu was
4973 * always on the upper left side of the application.
4976 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4979 POPUPMENU *itemMenu;
4983 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4985 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4986 referenceHwnd = hwnd;
4990 itemMenu = MENU_GetMenu(hMenu);
4991 if (itemMenu == NULL)
4994 if(itemMenu->hWnd == 0)
4996 referenceHwnd = itemMenu->hWnd;
4999 if ((rect == NULL) || (item == NULL))
5004 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5010 /**********************************************************************
5011 * SetMenuInfo (USER32.@)
5014 * MIM_APPLYTOSUBMENUS
5015 * actually use the items to draw the menu
5017 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5021 TRACE("(%p %p)\n", hMenu, lpmi);
5023 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5026 if (lpmi->fMask & MIM_BACKGROUND)
5027 menu->hbrBack = lpmi->hbrBack;
5029 if (lpmi->fMask & MIM_HELPID)
5030 menu->dwContextHelpID = lpmi->dwContextHelpID;
5032 if (lpmi->fMask & MIM_MAXHEIGHT)
5033 menu->cyMax = lpmi->cyMax;
5035 if (lpmi->fMask & MIM_MENUDATA)
5036 menu->dwMenuData = lpmi->dwMenuData;
5038 if (lpmi->fMask & MIM_STYLE)
5040 menu->dwStyle = lpmi->dwStyle;
5041 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5042 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5043 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5051 /**********************************************************************
5052 * GetMenuInfo (USER32.@)
5058 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5061 TRACE("(%p %p)\n", hMenu, lpmi);
5063 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5066 if (lpmi->fMask & MIM_BACKGROUND)
5067 lpmi->hbrBack = menu->hbrBack;
5069 if (lpmi->fMask & MIM_HELPID)
5070 lpmi->dwContextHelpID = menu->dwContextHelpID;
5072 if (lpmi->fMask & MIM_MAXHEIGHT)
5073 lpmi->cyMax = menu->cyMax;
5075 if (lpmi->fMask & MIM_MENUDATA)
5076 lpmi->dwMenuData = menu->dwMenuData;
5078 if (lpmi->fMask & MIM_STYLE)
5079 lpmi->dwStyle = menu->dwStyle;
5087 /**********************************************************************
5088 * SetMenuContextHelpId (USER32.@)
5090 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5094 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5096 if ((menu = MENU_GetMenu(hMenu)))
5098 menu->dwContextHelpID = dwContextHelpID;
5105 /**********************************************************************
5106 * GetMenuContextHelpId (USER32.@)
5108 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5112 TRACE("(%p)\n", hMenu);
5114 if ((menu = MENU_GetMenu(hMenu)))
5116 return menu->dwContextHelpID;
5121 /**********************************************************************
5122 * MenuItemFromPoint (USER32.@)
5124 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5126 POPUPMENU *menu = MENU_GetMenu(hMenu);
5129 /*FIXME: Do we have to handle hWnd here? */
5130 if (!menu) return -1;
5131 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5136 /**********************************************************************
5137 * translate_accelerator
5139 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5140 BYTE fVirt, WORD key, WORD cmd )
5145 if (wParam != key) return FALSE;
5147 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5148 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5149 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5151 if (message == WM_CHAR || message == WM_SYSCHAR)
5153 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5155 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5161 if(fVirt & FVIRTKEY)
5163 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5164 wParam, 0xff & HIWORD(lParam));
5166 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5167 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5171 if (!(lParam & 0x01000000)) /* no special_key */
5173 if ((fVirt & FALT) && (lParam & 0x20000000))
5174 { /* ^^ ALT pressed */
5175 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5184 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5188 HMENU hMenu, hSubMenu, hSysMenu;
5189 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5191 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5192 hSysMenu = get_win_sys_menu( hWnd );
5194 /* find menu item and ask application to initialize it */
5195 /* 1. in the system menu */
5196 hSubMenu = hSysMenu;
5198 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5202 if (!IsWindowEnabled(hWnd))
5206 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5207 if(hSubMenu != hSysMenu)
5209 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5210 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5211 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5213 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5216 else /* 2. in the window's menu */
5220 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5224 if (!IsWindowEnabled(hWnd))
5228 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5229 if(hSubMenu != hMenu)
5231 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5232 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5233 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5235 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5242 if (uSysStat != (UINT)-1)
5244 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5251 if (uStat != (UINT)-1)
5257 if (uStat & (MF_DISABLED|MF_GRAYED))
5269 if( mesg==WM_COMMAND )
5271 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5272 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5274 else if( mesg==WM_SYSCOMMAND )
5276 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5277 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5281 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5282 * #0: unknown (please report!)
5283 * #1: for WM_KEYUP,WM_SYSKEYUP
5284 * #2: mouse is captured
5285 * #3: window is disabled
5286 * #4: it's a disabled system menu option
5287 * #5: it's a menu option, but window is iconic
5288 * #6: it's a menu option, but disabled
5290 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5292 ERR_(accel)(" unknown reason - please report!\n");
5297 /**********************************************************************
5298 * TranslateAcceleratorA (USER32.@)
5299 * TranslateAccelerator (USER32.@)
5301 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5304 LPACCEL16 lpAccelTbl;
5308 if (!hWnd || !msg) return 0;
5310 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5312 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5316 wParam = msg->wParam;
5318 switch (msg->message)
5327 char ch = LOWORD(wParam);
5329 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5330 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5338 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5339 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5343 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5344 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5346 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5351 /**********************************************************************
5352 * TranslateAcceleratorW (USER32.@)
5354 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5357 LPACCEL16 lpAccelTbl;
5360 if (!hWnd || !msg) return 0;
5362 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5364 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5368 switch (msg->message)
5380 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5381 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5385 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5386 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5388 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);