4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 /* Popup menu structure */
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
130 #define MENU_MAGIC 0x554d /* 'MU' */
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class =
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
233 TRACE( ", Sub=%p", mp->hSubMenu);
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
248 TRACE( "+0x%x", flags);
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
262 TRACE( "+0x%x", flags);
265 TRACE( ", Chk=%p", mp->hCheckBit);
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
269 TRACE( ", Text=%s", debugstr_w(mp->text));
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
282 TRACE(" %s\n", postfix);
289 /***********************************************************************
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
298 if (menu == OBJ_OTHER_PROCESS)
300 WARN( "other process menu %p?\n", hMenu);
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
308 /***********************************************************************
311 * Get the system menu of a window
313 static HMENU get_win_sys_menu( HWND hwnd )
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
320 WIN_ReleasePtr( win );
325 /***********************************************************************
328 static HFONT get_menu_font( BOOL bold )
330 static HFONT hMenuFont, hMenuFontBold;
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
336 NONCLIENTMETRICSW ncm;
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
352 /* another thread beat us to it */
360 /***********************************************************************
363 static HBITMAP get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap;
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap;
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap;
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap;
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap;
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
415 /***********************************************************************
418 * Return the default system menu.
420 static HMENU MENU_CopySysPopup(BOOL mdi)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
428 MENUITEMINFOW miteminfo;
429 POPUPMENU* menu = MENU_GetMenu(hMenu);
430 menu->wFlags |= MF_SYSMENU | MF_POPUP;
431 /* decorate the menu with bitmaps */
432 minfo.cbSize = sizeof( MENUINFO);
433 minfo.dwStyle = MNS_CHECKORBMP;
434 minfo.fMask = MIM_STYLE;
435 SetMenuInfo( hMenu, &minfo);
436 miteminfo.cbSize = sizeof( MENUITEMINFOW);
437 miteminfo.fMask = MIIM_BITMAP;
438 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
439 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
440 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
441 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
443 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
446 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
449 ERR("Unable to load default system menu\n" );
451 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
457 /**********************************************************************
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
466 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
471 if ((hMenu = CreateMenu()))
473 POPUPMENU *menu = MENU_GetMenu(hMenu);
474 menu->wFlags = MF_SYSMENU;
475 menu->hWnd = WIN_GetFullHandle( hWnd );
476 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
480 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
481 hPopupMenu = MENU_CopySysPopup(TRUE);
483 hPopupMenu = MENU_CopySysPopup(FALSE);
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
501 DestroyMenu( hMenu );
503 ERR("failed to load system menu!\n");
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
529 /* The menu item must keep its state if it's disabled */
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
540 *****************************************************************************/
542 static UINT MENU_GetStartOfNextColumn(
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
549 return NO_SELECTED_ITEM;
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
560 return NO_SELECTED_ITEM;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
569 *****************************************************************************/
571 static UINT MENU_GetStartOfPrevColumn(
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
578 return NO_SELECTED_ITEM;
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
583 /* Find the start of the column */
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
590 return NO_SELECTED_ITEM;
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
597 TRACE("ret %d.\n", i );
604 /***********************************************************************
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
628 if (item->fType & MF_POPUP)
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
637 else if (item->wID == *nPos)
639 /* fallback to this item if nothing else found */
644 else if (item->wID == *nPos)
653 *nPos = fallback_pos;
658 /***********************************************************************
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
688 return NO_SELECTED_ITEM;
691 /***********************************************************************
694 static void MENU_FreeItemData( MENUITEM* item )
697 HeapFree( GetProcessHeap(), 0, item->text );
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
708 if (menu->bScrolling)
710 UINT arrow_bitmap_height;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
737 else pt.x -= rect.left;
740 for (i = 0; i < menu->nItems; i++, item++)
743 MENU_AdjustMenuItemRect(menu, &rect);
744 if (PtInRect(&rect, pt))
754 /***********************************************************************
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
760 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
761 WCHAR key, BOOL forceMenuChar )
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
765 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
769 POPUPMENU *menu = MENU_GetMenu( hmenu );
770 MENUITEM *item = menu->items;
776 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
778 for (i = 0; i < menu->nItems; i++, item++)
782 const WCHAR *p = item->text - 2;
785 const WCHAR *q = p + 2;
786 p = strchrW (q, '&');
787 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
789 while (p != NULL && p [1] == '&');
790 if (p && (toupperW(p[1]) == toupperW(key))) return i;
794 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
795 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
796 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
797 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
803 /***********************************************************************
804 * MENU_GetBitmapItemSize
806 * Get the size of a bitmap item.
808 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
812 HBITMAP bmp = lpitem->hbmpItem;
814 size->cx = size->cy = 0;
816 /* check if there is a magic menu item associated with this item */
817 switch( (INT_PTR) bmp )
819 case (INT_PTR)HBMMENU_CALLBACK:
821 MEASUREITEMSTRUCT measItem;
822 measItem.CtlType = ODT_MENU;
824 measItem.itemID = lpitem->wID;
825 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
826 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
827 measItem.itemData = lpitem->dwItemData;
828 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
829 size->cx = measItem.itemWidth;
830 size->cy = measItem.itemHeight;
834 case (INT_PTR)HBMMENU_SYSTEM:
835 if (lpitem->dwItemData)
837 bmp = (HBITMAP)lpitem->dwItemData;
841 case (INT_PTR)HBMMENU_MBAR_RESTORE:
842 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
843 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
844 case (INT_PTR)HBMMENU_MBAR_CLOSE:
845 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
846 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
849 case (INT_PTR)HBMMENU_POPUP_CLOSE:
850 case (INT_PTR)HBMMENU_POPUP_RESTORE:
851 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
852 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
853 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
854 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
857 if (GetObjectW(bmp, sizeof(bm), &bm ))
859 size->cx = bm.bmWidth;
860 size->cy = bm.bmHeight;
864 /***********************************************************************
865 * MENU_DrawBitmapItem
867 * Draw a bitmap item.
869 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
870 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
876 int w = rect->right - rect->left;
877 int h = rect->bottom - rect->top;
880 HBITMAP hbmToDraw = lpitem->hbmpItem;
883 /* Check if there is a magic menu item associated with this item */
884 if (IS_MAGIC_BITMAP(hbmToDraw))
890 switch((INT_PTR)hbmToDraw)
892 case (INT_PTR)HBMMENU_SYSTEM:
893 if (lpitem->dwItemData)
895 bmp = (HBITMAP)lpitem->dwItemData;
896 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
900 static HBITMAP hBmpSysMenu;
902 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
904 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
905 /* only use right half of the bitmap */
906 bmp_xoffset = bm.bmWidth / 2;
907 bm.bmWidth -= bmp_xoffset;
910 case (INT_PTR)HBMMENU_MBAR_RESTORE:
911 flags = DFCS_CAPTIONRESTORE;
913 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
914 flags = DFCS_CAPTIONMIN;
916 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
917 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
919 case (INT_PTR)HBMMENU_MBAR_CLOSE:
920 flags = DFCS_CAPTIONCLOSE;
922 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
923 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
925 case (INT_PTR)HBMMENU_CALLBACK:
927 DRAWITEMSTRUCT drawItem;
928 drawItem.CtlType = ODT_MENU;
930 drawItem.itemID = lpitem->wID;
931 drawItem.itemAction = odaction;
932 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
933 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
934 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
935 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
936 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
937 drawItem.hwndItem = (HWND)hmenu;
939 drawItem.itemData = lpitem->dwItemData;
940 drawItem.rcItem = *rect;
941 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
945 case (INT_PTR)HBMMENU_POPUP_CLOSE:
948 case (INT_PTR)HBMMENU_POPUP_RESTORE:
951 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
954 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
958 FIXME("Magic %p not implemented\n", hbmToDraw);
963 /* draw the magic bitmaps using marlett font characters */
964 /* FIXME: fontsize and the position (x,y) could probably be better */
965 HFONT hfont, hfontsav;
966 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
967 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
968 { 'M','a','r','l','e','t','t',0 } };
969 logfont.lfHeight = min( h, w) - 5 ;
970 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
971 hfont = CreateFontIndirectW( &logfont);
972 hfontsav = SelectObject(hdc, hfont);
973 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
974 SelectObject(hdc, hfontsav);
975 DeleteObject( hfont);
980 InflateRect( &r, -1, -1 );
981 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
982 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
987 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
990 hdcMem = CreateCompatibleDC( hdc );
991 SelectObject( hdcMem, bmp );
993 /* handle fontsize > bitmap_height */
994 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
996 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
997 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
998 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
999 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1004 /***********************************************************************
1007 * Calculate the size of the menu item and store it in lpitem->rect.
1009 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1010 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1013 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1014 UINT arrow_bitmap_width;
1018 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1019 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1020 (menuBar ? " (MenuBar)" : ""));
1022 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1023 arrow_bitmap_width = bm.bmWidth;
1025 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1026 if( !menucharsize.cx ) {
1027 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1028 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1029 * but it is unlikely an application will depend on that */
1030 ODitemheight = HIWORD( GetDialogBaseUnits());
1033 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1035 if (lpitem->fType & MF_OWNERDRAW)
1037 MEASUREITEMSTRUCT mis;
1038 mis.CtlType = ODT_MENU;
1040 mis.itemID = lpitem->wID;
1041 mis.itemData = lpitem->dwItemData;
1042 mis.itemHeight = ODitemheight;
1044 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1045 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1046 * width of a menufont character to the width of an owner-drawn menu.
1048 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1050 /* under at least win95 you seem to be given a standard
1051 height for the menu and the height value is ignored */
1052 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1054 lpitem->rect.bottom += mis.itemHeight;
1056 TRACE("id=%04lx size=%dx%d\n",
1057 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1058 lpitem->rect.bottom-lpitem->rect.top);
1062 if (lpitem->fType & MF_SEPARATOR)
1064 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1066 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1074 if (lpitem->hbmpItem) {
1077 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1078 /* Keep the size of the bitmap in callback mode to be able
1079 * to draw it correctly */
1080 lpitem->bmpsize = size;
1081 lppop->textOffset = max( lppop->textOffset, size.cx);
1082 lpitem->rect.right += size.cx + 2;
1083 itemheight = size.cy + 2;
1085 if( !(lppop->dwStyle & MNS_NOCHECK))
1086 lpitem->rect.right += check_bitmap_width;
1087 lpitem->rect.right += 4 + menucharsize.cx;
1088 lpitem->xTab = lpitem->rect.right;
1089 lpitem->rect.right += arrow_bitmap_width;
1090 } else if (lpitem->hbmpItem) { /* menuBar */
1093 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1094 lpitem->bmpsize = size;
1095 lpitem->rect.right += size.cx;
1096 if( lpitem->text) lpitem->rect.right += 2;
1097 itemheight = size.cy;
1100 /* it must be a text item - unless it's the system menu */
1101 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1102 HFONT hfontOld = NULL;
1103 RECT rc = lpitem->rect;
1104 LONG txtheight, txtwidth;
1106 if ( lpitem->fState & MFS_DEFAULT ) {
1107 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1110 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1111 DT_SINGLELINE|DT_CALCRECT);
1112 lpitem->rect.right += rc.right - rc.left;
1113 itemheight = max( max( itemheight, txtheight),
1114 GetSystemMetrics( SM_CYMENU) - 1);
1115 lpitem->rect.right += 2 * menucharsize.cx;
1117 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1120 int n = (int)( p - lpitem->text);
1121 /* Item contains a tab (only meaningful in popup menus) */
1122 /* get text size before the tab */
1123 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1124 DT_SINGLELINE|DT_CALCRECT);
1125 txtwidth = rc.right - rc.left;
1126 p += 1; /* advance past the Tab */
1127 /* get text size after the tab */
1128 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 lpitem->xTab += txtwidth;
1131 txtheight = max( txtheight, tmpheight);
1132 txtwidth += menucharsize.cx + /* space for the tab */
1133 tmprc.right - tmprc.left; /* space for the short cut */
1135 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1136 DT_SINGLELINE|DT_CALCRECT);
1137 txtwidth = rc.right - rc.left;
1138 lpitem->xTab += txtwidth;
1140 lpitem->rect.right += 2 + txtwidth;
1141 itemheight = max( itemheight,
1142 max( txtheight + 2, menucharsize.cy + 4));
1144 if (hfontOld) SelectObject (hdc, hfontOld);
1145 } else if( menuBar) {
1146 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1148 lpitem->rect.bottom += itemheight;
1149 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1153 /***********************************************************************
1154 * MENU_GetMaxPopupHeight
1157 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1160 return lppop->cyMax;
1161 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1165 /***********************************************************************
1166 * MENU_PopupMenuCalcSize
1168 * Calculate the size of a popup menu.
1170 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1175 int textandbmp = FALSE;
1176 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1178 lppop->Width = lppop->Height = 0;
1179 if (lppop->nItems == 0) return;
1182 SelectObject( hdc, get_menu_font(FALSE));
1187 lppop->textOffset = 0;
1189 while (start < lppop->nItems)
1191 lpitem = &lppop->items[start];
1193 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1194 orgX += MENU_COL_SPACE;
1195 orgY = MENU_TOP_MARGIN;
1197 maxTab = maxTabWidth = 0;
1198 /* Parse items until column break or end of menu */
1199 for (i = start; i < lppop->nItems; i++, lpitem++)
1202 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1204 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1205 maxX = max( maxX, lpitem->rect.right );
1206 orgY = lpitem->rect.bottom;
1207 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1209 maxTab = max( maxTab, lpitem->xTab );
1210 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1212 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1215 /* Finish the column (set all items to the largest width found) */
1216 maxX = max( maxX, maxTab + maxTabWidth );
1217 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1219 lpitem->rect.right = maxX;
1220 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1221 lpitem->xTab = maxTab;
1224 lppop->Height = max( lppop->Height, orgY );
1227 lppop->Width = maxX;
1228 /* if none of the items have both text and bitmap then
1229 * the text and bitmaps are all aligned on the left. If there is at
1230 * least one item with both text and bitmap then bitmaps are
1231 * on the left and texts left aligned with the right hand side
1233 if( !textandbmp) lppop->textOffset = 0;
1235 /* space for 3d border */
1236 lppop->Height += MENU_BOTTOM_MARGIN;
1239 /* Adjust popup height if it exceeds maximum */
1240 maxHeight = MENU_GetMaxPopupHeight(lppop);
1241 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1242 if (lppop->Height >= maxHeight)
1244 lppop->Height = maxHeight;
1245 lppop->bScrolling = TRUE;
1249 lppop->bScrolling = FALSE;
1252 ReleaseDC( 0, hdc );
1256 /***********************************************************************
1257 * MENU_MenuBarCalcSize
1259 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1260 * height is off by 1 pixel which causes lengthy window relocations when
1261 * active document window is maximized/restored.
1263 * Calculate the size of the menu bar.
1265 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1266 LPPOPUPMENU lppop, HWND hwndOwner )
1269 UINT start, i, helpPos;
1270 int orgX, orgY, maxY;
1272 if ((lprect == NULL) || (lppop == NULL)) return;
1273 if (lppop->nItems == 0) return;
1274 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1275 lppop->Width = lprect->right - lprect->left;
1277 maxY = lprect->top+1;
1280 lppop->textOffset = 0;
1281 while (start < lppop->nItems)
1283 lpitem = &lppop->items[start];
1284 orgX = lprect->left;
1287 /* Parse items until line break or end of menu */
1288 for (i = start; i < lppop->nItems; i++, lpitem++)
1290 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1292 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1294 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1295 debug_print_menuitem (" item: ", lpitem, "");
1296 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1298 if (lpitem->rect.right > lprect->right)
1300 if (i != start) break;
1301 else lpitem->rect.right = lprect->right;
1303 maxY = max( maxY, lpitem->rect.bottom );
1304 orgX = lpitem->rect.right;
1307 /* Finish the line (set all items to the largest height found) */
1308 while (start < i) lppop->items[start++].rect.bottom = maxY;
1311 lprect->bottom = maxY;
1312 lppop->Height = lprect->bottom - lprect->top;
1314 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1315 /* the last item (if several lines, only move the last line) */
1316 if (helpPos == ~0U) return;
1317 lpitem = &lppop->items[lppop->nItems-1];
1318 orgY = lpitem->rect.top;
1319 orgX = lprect->right;
1320 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1321 if (lpitem->rect.top != orgY) break; /* Other line */
1322 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1323 lpitem->rect.left += orgX - lpitem->rect.right;
1324 lpitem->rect.right = orgX;
1325 orgX = lpitem->rect.left;
1330 /***********************************************************************
1331 * MENU_DrawScrollArrows
1333 * Draw scroll arrows.
1336 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1338 HDC hdcMem = CreateCompatibleDC(hdc);
1339 HBITMAP hOrigBitmap;
1340 UINT arrow_bitmap_width, arrow_bitmap_height;
1344 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1345 arrow_bitmap_width = bmp.bmWidth;
1346 arrow_bitmap_height = bmp.bmHeight;
1349 if (lppop->nScrollPos)
1350 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1352 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1355 rect.right = lppop->Width;
1356 rect.bottom = arrow_bitmap_height;
1357 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1358 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1359 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1360 rect.top = lppop->Height - arrow_bitmap_height;
1361 rect.bottom = lppop->Height;
1362 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1363 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1364 SelectObject(hdcMem, get_down_arrow_bitmap());
1366 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1367 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1368 lppop->Height - arrow_bitmap_height,
1369 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1370 SelectObject(hdcMem, hOrigBitmap);
1375 /***********************************************************************
1378 * Draws the popup-menu arrow.
1380 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1381 UINT arrow_bitmap_height)
1383 HDC hdcMem = CreateCompatibleDC( hdc );
1384 HBITMAP hOrigBitmap;
1386 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1387 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1388 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1389 arrow_bitmap_width, arrow_bitmap_height,
1390 hdcMem, 0, 0, SRCCOPY );
1391 SelectObject( hdcMem, hOrigBitmap );
1394 /***********************************************************************
1397 * Draw a single menu item.
1399 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1400 UINT height, BOOL menuBar, UINT odaction )
1403 BOOL flat_menu = FALSE;
1405 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1406 POPUPMENU *menu = MENU_GetMenu(hmenu);
1409 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1413 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1414 arrow_bitmap_width = bmp.bmWidth;
1415 arrow_bitmap_height = bmp.bmHeight;
1418 if (lpitem->fType & MF_SYSMENU)
1420 if( !IsIconic(hwnd) )
1421 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1425 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1426 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1430 if (lpitem->fState & MF_HILITE)
1432 if(menuBar && !flat_menu) {
1433 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1434 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1436 if(lpitem->fState & MF_GRAYED)
1437 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1439 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1440 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1445 if (lpitem->fState & MF_GRAYED)
1446 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1448 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1449 SetBkColor( hdc, GetSysColor( bkgnd ) );
1452 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1453 rect = lpitem->rect;
1454 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1456 if (lpitem->fType & MF_OWNERDRAW)
1459 ** Experimentation under Windows reveals that an owner-drawn
1460 ** menu is given the rectangle which includes the space it requested
1461 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1462 ** and a popup-menu arrow. This is the value of lpitem->rect.
1463 ** Windows will leave all drawing to the application except for
1464 ** the popup-menu arrow. Windows always draws that itself, after
1465 ** the menu owner has finished drawing.
1469 dis.CtlType = ODT_MENU;
1471 dis.itemID = lpitem->wID;
1472 dis.itemData = lpitem->dwItemData;
1474 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1475 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1476 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1477 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1478 dis.hwndItem = (HWND)hmenu;
1481 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1482 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1483 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1484 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1485 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1486 /* Draw the popup-menu arrow */
1487 if (lpitem->fType & MF_POPUP)
1488 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1489 arrow_bitmap_height);
1493 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1495 if (lpitem->fState & MF_HILITE)
1499 InflateRect (&rect, -1, -1);
1500 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1501 InflateRect (&rect, 1, 1);
1502 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1507 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1509 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1513 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1515 SetBkMode( hdc, TRANSPARENT );
1517 /* vertical separator */
1518 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1523 rc.left -= MENU_COL_SPACE / 2 + 1;
1525 rc.bottom = height - 3;
1528 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1529 MoveToEx( hdc, rc.left, rc.top, NULL );
1530 LineTo( hdc, rc.left, rc.bottom );
1531 SelectObject( hdc, oldPen );
1534 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1537 /* horizontal separator */
1538 if (lpitem->fType & MF_SEPARATOR)
1545 rc.top = ( rc.top + rc.bottom) / 2;
1548 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1549 MoveToEx( hdc, rc.left, rc.top, NULL );
1550 LineTo( hdc, rc.right, rc.top );
1551 SelectObject( hdc, oldPen );
1554 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1558 /* helper lines for debugging */
1559 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1560 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1561 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1562 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1565 if (lpitem->hbmpItem) {
1566 /* calculate the bitmap rectangle in coordinates relative
1567 * to the item rectangle */
1569 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1572 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1574 else if (menu->dwStyle & MNS_NOCHECK)
1576 else if (menu->dwStyle & MNS_CHECKORBMP)
1579 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1580 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1581 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1584 bmprc.top = (rect.bottom - rect.top -
1585 lpitem->bmpsize.cy) / 2;
1586 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1592 INT y = rect.top + rect.bottom;
1594 int checked = FALSE;
1595 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1596 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1597 /* Draw the check mark
1600 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1602 if( !(menu->dwStyle & MNS_NOCHECK)) {
1603 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1604 lpitem->hUnCheckBit;
1605 if (bm) /* we have a custom bitmap */
1607 HDC hdcMem = CreateCompatibleDC( hdc );
1609 SelectObject( hdcMem, bm );
1610 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1611 check_bitmap_width, check_bitmap_height,
1612 hdcMem, 0, 0, SRCCOPY );
1616 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1619 HBITMAP bm = CreateBitmap( check_bitmap_width,
1620 check_bitmap_height, 1, 1, NULL );
1621 HDC hdcMem = CreateCompatibleDC( hdc );
1623 SelectObject( hdcMem, bm );
1624 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1625 DrawFrameControl( hdcMem, &r, DFC_MENU,
1626 (lpitem->fType & MFT_RADIOCHECK) ?
1627 DFCS_MENUBULLET : DFCS_MENUCHECK );
1628 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1629 hdcMem, 0, 0, SRCCOPY );
1635 if( lpitem->hbmpItem &&
1636 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1638 /* some applications make this assumption on the DC's origin */
1639 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1640 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1642 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1644 /* Draw the popup-menu arrow */
1645 if (lpitem->fType & MF_POPUP)
1646 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1647 arrow_bitmap_height);
1649 if( !(menu->dwStyle & MNS_NOCHECK))
1650 rect.left += check_bitmap_width;
1651 rect.right -= arrow_bitmap_width;
1653 else if( lpitem->hbmpItem)
1654 { /* Draw the bitmap */
1657 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1658 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1660 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1662 /* process text if present */
1668 UINT uFormat = (menuBar) ?
1669 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1670 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1672 if( !(menu->dwStyle & MNS_CHECKORBMP))
1673 rect.left += menu->textOffset;
1675 if ( lpitem->fState & MFS_DEFAULT )
1677 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1681 if( lpitem->hbmpItem)
1682 rect.left += lpitem->bmpsize.cx;
1683 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1684 rect.left += menucharsize.cx;
1685 rect.right -= menucharsize.cx;
1688 for (i = 0; lpitem->text[i]; i++)
1689 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1692 if(lpitem->fState & MF_GRAYED)
1694 if (!(lpitem->fState & MF_HILITE) )
1696 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1697 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1698 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1699 --rect.left; --rect.top; --rect.right; --rect.bottom;
1701 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1704 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1706 /* paint the shortcut text */
1707 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1709 if (lpitem->text[i] == '\t')
1711 rect.left = lpitem->xTab;
1712 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1716 rect.right = lpitem->xTab;
1717 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1720 if(lpitem->fState & MF_GRAYED)
1722 if (!(lpitem->fState & MF_HILITE) )
1724 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1725 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1726 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1727 --rect.left; --rect.top; --rect.right; --rect.bottom;
1729 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1731 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1735 SelectObject (hdc, hfontOld);
1740 /***********************************************************************
1741 * MENU_DrawPopupMenu
1743 * Paint a popup menu.
1745 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1747 HBRUSH hPrevBrush = 0;
1750 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1752 GetClientRect( hwnd, &rect );
1754 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1755 && (SelectObject( hdc, get_menu_font(FALSE))))
1759 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1761 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1765 BOOL flat_menu = FALSE;
1767 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1769 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1771 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1773 if( (menu = MENU_GetMenu( hmenu )))
1775 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1776 /* draw menu items */
1783 for( u = menu->nItems; u > 0; u--, item++)
1784 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1785 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1787 /* draw scroll arrows */
1788 if (menu->bScrolling)
1789 MENU_DrawScrollArrows(menu, hdc);
1793 SelectObject( hdc, hPrevBrush );
1798 /***********************************************************************
1801 * Paint a menu bar. Returns the height of the menu bar.
1802 * called from [windows/nonclient.c]
1804 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1809 HMENU hMenu = GetMenu(hwnd);
1811 lppop = MENU_GetMenu( hMenu );
1812 if (lppop == NULL || lprect == NULL)
1814 return GetSystemMetrics(SM_CYMENU);
1819 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1821 if (lppop->Height == 0)
1822 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1824 lprect->bottom = lprect->top + lppop->Height;
1826 if (hfontOld) SelectObject( hDC, hfontOld);
1827 return lppop->Height;
1830 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1834 /***********************************************************************
1837 * Display a popup menu.
1839 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1840 INT x, INT y, INT xanchor, INT yanchor )
1849 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1850 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1852 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1853 if (menu->FocusedItem != NO_SELECTED_ITEM)
1855 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1856 menu->FocusedItem = NO_SELECTED_ITEM;
1859 /* store the owner for DrawItem */
1860 if (!IsWindow( hwndOwner ))
1862 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1865 menu->hwndOwner = hwndOwner;
1867 menu->nScrollPos = 0;
1868 MENU_PopupMenuCalcSize( menu );
1870 /* adjust popup menu pos so that it fits within the desktop */
1872 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1873 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1875 /* FIXME: should use item rect */
1878 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1879 info.cbSize = sizeof(info);
1880 GetMonitorInfoW( monitor, &info );
1882 if (flags & TPM_LAYOUTRTL)
1884 ex_style = WS_EX_LAYOUTRTL;
1885 flags ^= TPM_RIGHTALIGN;
1888 if( flags & TPM_RIGHTALIGN ) x -= width;
1889 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1891 if( flags & TPM_BOTTOMALIGN ) y -= height;
1892 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1894 if( x + width > info.rcWork.right)
1896 if( xanchor && x >= width - xanchor )
1897 x -= width - xanchor;
1899 if( x + width > info.rcWork.right)
1900 x = info.rcWork.right - width;
1902 if( x < info.rcWork.left ) x = info.rcWork.left;
1904 if( y + height > info.rcWork.bottom)
1906 if( yanchor && y >= height + yanchor )
1907 y -= height + yanchor;
1909 if( y + height > info.rcWork.bottom)
1910 y = info.rcWork.bottom - height;
1912 if( y < info.rcWork.top ) y = info.rcWork.top;
1914 /* NOTE: In Windows, top menu popup is not owned. */
1915 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1916 WS_POPUP, x, y, width, height,
1917 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1919 if( !menu->hWnd ) return FALSE;
1921 top_popup = menu->hWnd;
1922 top_popup_hmenu = hmenu;
1924 /* Display the window */
1926 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1927 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1928 UpdateWindow( menu->hWnd );
1933 /***********************************************************************
1934 * MENU_EnsureMenuItemVisible
1937 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1939 if (lppop->bScrolling)
1941 MENUITEM *item = &lppop->items[wIndex];
1942 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1943 UINT nOldPos = lppop->nScrollPos;
1945 UINT arrow_bitmap_height;
1948 GetClientRect(lppop->hWnd, &rc);
1950 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1951 arrow_bitmap_height = bmp.bmHeight;
1953 rc.top += arrow_bitmap_height;
1954 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1956 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1957 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1960 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1961 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1962 MENU_DrawScrollArrows(lppop, hdc);
1964 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1966 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1967 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1968 MENU_DrawScrollArrows(lppop, hdc);
1974 /***********************************************************************
1977 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1978 BOOL sendMenuSelect, HMENU topmenu )
1983 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1985 lppop = MENU_GetMenu( hmenu );
1986 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1988 if (lppop->FocusedItem == wIndex) return;
1989 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1990 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1992 top_popup = lppop->hWnd;
1993 top_popup_hmenu = hmenu;
1996 SelectObject( hdc, get_menu_font(FALSE));
1998 /* Clear previous highlighted item */
1999 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2001 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2002 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2003 lppop->Height, !(lppop->wFlags & MF_POPUP),
2007 /* Highlight new item (if any) */
2008 lppop->FocusedItem = wIndex;
2009 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2011 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2012 lppop->items[wIndex].fState |= MF_HILITE;
2013 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2014 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2015 &lppop->items[wIndex], lppop->Height,
2016 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2020 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2021 SendMessageW( hwndOwner, WM_MENUSELECT,
2022 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2023 ip->fType | ip->fState |
2024 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2027 else if (sendMenuSelect) {
2030 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2031 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2032 MENUITEM *ip = &ptm->items[pos];
2033 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2034 ip->fType | ip->fState |
2035 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2039 ReleaseDC( lppop->hWnd, hdc );
2043 /***********************************************************************
2044 * MENU_MoveSelection
2046 * Moves currently selected item according to the offset parameter.
2047 * If there is no selection then it should select the last item if
2048 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2050 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2055 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2057 menu = MENU_GetMenu( hmenu );
2058 if ((!menu) || (!menu->items)) return;
2060 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2062 if( menu->nItems == 1 ) return; else
2063 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2065 if (!(menu->items[i].fType & MF_SEPARATOR))
2067 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2072 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2073 i >= 0 && i < menu->nItems ; i += offset)
2074 if (!(menu->items[i].fType & MF_SEPARATOR))
2076 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2082 /**********************************************************************
2085 * Insert (allocate) a new item into a menu.
2087 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2092 if (!(menu = MENU_GetMenu(hMenu)))
2095 /* Find where to insert new item */
2097 if (flags & MF_BYPOSITION) {
2098 if (pos > menu->nItems)
2101 if (!MENU_FindItem( &hMenu, &pos, flags ))
2104 if (!(menu = MENU_GetMenu( hMenu )))
2109 /* Make sure that MDI system buttons stay on the right side.
2110 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2111 * regardless of their id.
2113 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2114 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2117 TRACE("inserting at %u flags %x\n", pos, flags);
2119 /* Create new items array */
2121 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2124 WARN("allocation failed\n" );
2127 if (menu->nItems > 0)
2129 /* Copy the old array into the new one */
2130 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2131 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2132 (menu->nItems-pos)*sizeof(MENUITEM) );
2133 HeapFree( GetProcessHeap(), 0, menu->items );
2135 menu->items = newItems;
2137 memset( &newItems[pos], 0, sizeof(*newItems) );
2138 menu->Height = 0; /* force size recalculate */
2139 return &newItems[pos];
2143 /**********************************************************************
2144 * MENU_ParseResource
2146 * Parse a standard menu resource and add items to the menu.
2147 * Return a pointer to the end of the resource.
2149 * NOTE: flags is equivalent to the mtOption field
2151 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2159 flags = GET_WORD(res);
2160 end_flag = flags & MF_END;
2161 /* Remove MF_END because it has the same value as MF_HILITE */
2163 res += sizeof(WORD);
2164 if (!(flags & MF_POPUP))
2167 res += sizeof(WORD);
2170 res += (strlenW(str) + 1) * sizeof(WCHAR);
2171 if (flags & MF_POPUP)
2173 HMENU hSubMenu = CreatePopupMenu();
2174 if (!hSubMenu) return NULL;
2175 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2176 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2178 else /* Not a popup */
2180 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2182 } while (!end_flag);
2187 /**********************************************************************
2188 * MENUEX_ParseResource
2190 * Parse an extended menu resource and add items to the menu.
2191 * Return a pointer to the end of the resource.
2193 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2199 mii.cbSize = sizeof(mii);
2200 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2201 mii.fType = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 mii.fState = GET_DWORD(res);
2204 res += sizeof(DWORD);
2205 mii.wID = GET_DWORD(res);
2206 res += sizeof(DWORD);
2207 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2208 res += sizeof(WORD);
2209 /* Align the text on a word boundary. */
2210 res += (~((UINT_PTR)res - 1)) & 1;
2211 mii.dwTypeData = (LPWSTR) res;
2212 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2213 /* Align the following fields on a dword boundary. */
2214 res += (~((UINT_PTR)res - 1)) & 3;
2216 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2217 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2219 if (resinfo & 1) { /* Pop-up? */
2220 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2221 res += sizeof(DWORD);
2222 mii.hSubMenu = CreatePopupMenu();
2225 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2226 DestroyMenu(mii.hSubMenu);
2229 mii.fMask |= MIIM_SUBMENU;
2230 mii.fType |= MF_POPUP;
2232 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2234 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2235 mii.wID, mii.fType);
2236 mii.fType |= MF_SEPARATOR;
2238 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2239 } while (!(resinfo & MF_END));
2244 /***********************************************************************
2247 * Return the handle of the selected sub-popup menu (if any).
2249 static HMENU MENU_GetSubPopup( HMENU hmenu )
2254 menu = MENU_GetMenu( hmenu );
2256 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2258 item = &menu->items[menu->FocusedItem];
2259 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2260 return item->hSubMenu;
2265 /***********************************************************************
2266 * MENU_HideSubPopups
2268 * Hide the sub-popup menus of this menu.
2270 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2271 BOOL sendMenuSelect, UINT wFlags )
2273 POPUPMENU *menu = MENU_GetMenu( hmenu );
2275 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2277 if (menu && top_popup)
2283 if (menu->FocusedItem != NO_SELECTED_ITEM)
2285 item = &menu->items[menu->FocusedItem];
2286 if (!(item->fType & MF_POPUP) ||
2287 !(item->fState & MF_MOUSESELECT)) return;
2288 item->fState &= ~MF_MOUSESELECT;
2289 hsubmenu = item->hSubMenu;
2292 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2293 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2294 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2295 DestroyWindow( submenu->hWnd );
2298 if (!(wFlags & TPM_NONOTIFY))
2299 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2300 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2305 /***********************************************************************
2308 * Display the sub-menu of the selected item of this menu.
2309 * Return the handle of the submenu, or hmenu if no submenu to display.
2311 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2312 BOOL selectFirst, UINT wFlags )
2319 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2321 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2323 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2325 item = &menu->items[menu->FocusedItem];
2326 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2329 /* message must be sent before using item,
2330 because nearly everything may be changed by the application ! */
2332 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2333 if (!(wFlags & TPM_NONOTIFY))
2334 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2335 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2337 item = &menu->items[menu->FocusedItem];
2340 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2341 if (!(item->fState & MF_HILITE))
2343 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2344 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2346 SelectObject( hdc, get_menu_font(FALSE));
2348 item->fState |= MF_HILITE;
2349 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2350 ReleaseDC( menu->hWnd, hdc );
2352 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2355 item->fState |= MF_MOUSESELECT;
2357 if (IS_SYSTEM_MENU(menu))
2359 MENU_InitSysMenuPopup(item->hSubMenu,
2360 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2361 GetClassLongW( menu->hWnd, GCL_STYLE));
2363 NC_GetSysPopupPos( menu->hWnd, &rect );
2364 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2365 rect.top = rect.bottom;
2366 rect.right = GetSystemMetrics(SM_CXSIZE);
2367 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2371 GetWindowRect( menu->hWnd, &rect );
2372 if (menu->wFlags & MF_POPUP)
2374 RECT rc = item->rect;
2376 MENU_AdjustMenuItemRect(menu, &rc);
2378 /* The first item in the popup menu has to be at the
2379 same y position as the focused menu item */
2380 if (wFlags & TPM_LAYOUTRTL)
2381 rect.left += GetSystemMetrics(SM_CXBORDER);
2383 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2384 rect.top += rc.top - MENU_TOP_MARGIN;
2385 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2386 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2387 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2391 if (wFlags & TPM_LAYOUTRTL)
2392 rect.left = rect.right - item->rect.left;
2394 rect.left += item->rect.left;
2395 rect.top += item->rect.bottom;
2396 rect.right = item->rect.right - item->rect.left;
2397 rect.bottom = item->rect.bottom - item->rect.top;
2401 /* use default alignment for submenus */
2402 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2404 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2405 rect.left, rect.top, rect.right, rect.bottom );
2407 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2408 return item->hSubMenu;
2413 /**********************************************************************
2416 HWND MENU_IsMenuActive(void)
2421 /**********************************************************************
2424 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2426 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2428 void MENU_EndMenu( HWND hwnd )
2431 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2432 if (menu && hwnd == menu->hwndOwner) EndMenu();
2435 /***********************************************************************
2438 * Walks menu chain trying to find a menu pt maps to.
2440 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2442 POPUPMENU *menu = MENU_GetMenu( hMenu );
2443 UINT item = menu->FocusedItem;
2446 /* try subpopup first (if any) */
2447 ret = (item != NO_SELECTED_ITEM &&
2448 (menu->items[item].fType & MF_POPUP) &&
2449 (menu->items[item].fState & MF_MOUSESELECT))
2450 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2452 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2454 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2455 if( menu->wFlags & MF_POPUP )
2457 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2459 else if (ht == HTSYSMENU)
2460 ret = get_win_sys_menu( menu->hWnd );
2461 else if (ht == HTMENU)
2462 ret = GetMenu( menu->hWnd );
2467 /***********************************************************************
2468 * MENU_ExecFocusedItem
2470 * Execute a menu item (for instance when user pressed Enter).
2471 * Return the wID of the executed item. Otherwise, -1 indicating
2472 * that no menu item was executed, -2 if a popup is shown;
2473 * Have to receive the flags for the TrackPopupMenu options to avoid
2474 * sending unwanted message.
2477 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2480 POPUPMENU *menu = MENU_GetMenu( hMenu );
2482 TRACE("%p hmenu=%p\n", pmt, hMenu);
2484 if (!menu || !menu->nItems ||
2485 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2487 item = &menu->items[menu->FocusedItem];
2489 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2491 if (!(item->fType & MF_POPUP))
2493 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2495 /* If TPM_RETURNCMD is set you return the id, but
2496 do not send a message to the owner */
2497 if(!(wFlags & TPM_RETURNCMD))
2499 if( menu->wFlags & MF_SYSMENU )
2500 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2501 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2505 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2507 if (dwStyle & MNS_NOTIFYBYPOS)
2508 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2511 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2519 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2526 /***********************************************************************
2527 * MENU_SwitchTracking
2529 * Helper function for menu navigation routines.
2531 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2536 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2538 if( pmt->hTopMenu != hPtMenu &&
2539 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2541 /* both are top level menus (system and menu-bar) */
2542 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2543 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2544 pmt->hTopMenu = hPtMenu;
2546 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2547 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2551 /***********************************************************************
2554 * Return TRUE if we can go on with menu tracking.
2556 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2558 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2563 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2566 if( IS_SYSTEM_MENU(ptmenu) )
2567 item = ptmenu->items;
2569 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2573 if( ptmenu->FocusedItem != id )
2574 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2576 /* If the popup menu is not already "popped" */
2577 if(!(item->fState & MF_MOUSESELECT ))
2579 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2584 /* Else the click was on the menu bar, finish the tracking */
2589 /***********************************************************************
2592 * Return the value of MENU_ExecFocusedItem if
2593 * the selected item was not a popup. Else open the popup.
2594 * A -1 return value indicates that we go on with menu tracking.
2597 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2599 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2604 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2607 if( IS_SYSTEM_MENU(ptmenu) )
2608 item = ptmenu->items;
2610 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2612 if( item && (ptmenu->FocusedItem == id ))
2614 debug_print_menuitem ("FocusedItem: ", item, "");
2616 if( !(item->fType & MF_POPUP) )
2618 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2619 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2620 return executedMenuId;
2623 /* If we are dealing with the top-level menu */
2624 /* and this is a click on an already "popped" item: */
2625 /* Stop the menu tracking and close the opened submenus */
2626 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2629 ptmenu->bTimeToHide = TRUE;
2635 /***********************************************************************
2638 * Return TRUE if we can go on with menu tracking.
2640 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2642 UINT id = NO_SELECTED_ITEM;
2643 POPUPMENU *ptmenu = NULL;
2647 ptmenu = MENU_GetMenu( hPtMenu );
2648 if( IS_SYSTEM_MENU(ptmenu) )
2651 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2654 if( id == NO_SELECTED_ITEM )
2656 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2657 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2660 else if( ptmenu->FocusedItem != id )
2662 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2663 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2669 /***********************************************************************
2672 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2674 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2676 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2679 /* When skipping left, we need to do something special after the
2681 if (vk == VK_LEFT && menu->FocusedItem == 0)
2685 /* When skipping right, for the non-system menu, we need to
2686 handle the last non-special menu item (ie skip any window
2687 icons such as MDI maximize, restore or close) */
2688 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2690 UINT i = menu->FocusedItem + 1;
2691 while (i < menu->nItems) {
2692 if ((menu->items[i].wID >= SC_SIZE &&
2693 menu->items[i].wID <= SC_RESTORE)) {
2697 if (i == menu->nItems) {
2701 /* When skipping right, we need to cater for the system menu */
2702 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2704 if (menu->FocusedItem == (menu->nItems - 1)) {
2711 MDINEXTMENU next_menu;
2716 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2717 next_menu.hmenuNext = 0;
2718 next_menu.hwndNext = 0;
2719 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2721 TRACE("%p [%p] -> %p [%p]\n",
2722 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2724 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2726 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2727 hNewWnd = pmt->hOwnerWnd;
2728 if( IS_SYSTEM_MENU(menu) )
2730 /* switch to the menu bar */
2732 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2736 menu = MENU_GetMenu( hNewMenu );
2737 id = menu->nItems - 1;
2739 /* Skip backwards over any system predefined icons,
2740 eg. MDI close, restore etc icons */
2742 (menu->items[id].wID >= SC_SIZE &&
2743 menu->items[id].wID <= SC_RESTORE)) id--;
2746 else if (style & WS_SYSMENU )
2748 /* switch to the system menu */
2749 hNewMenu = get_win_sys_menu( hNewWnd );
2753 else /* application returned a new menu to switch to */
2755 hNewMenu = next_menu.hmenuNext;
2756 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2758 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2760 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2762 if (style & WS_SYSMENU &&
2763 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2765 /* get the real system menu */
2766 hNewMenu = get_win_sys_menu(hNewWnd);
2768 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2770 /* FIXME: Not sure what to do here;
2771 * perhaps try to track hNewMenu as a popup? */
2773 TRACE(" -- got confused.\n");
2780 if( hNewMenu != pmt->hTopMenu )
2782 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2784 if( pmt->hCurrentMenu != pmt->hTopMenu )
2785 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2788 if( hNewWnd != pmt->hOwnerWnd )
2790 pmt->hOwnerWnd = hNewWnd;
2791 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2794 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2795 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2802 /***********************************************************************
2805 * The idea is not to show the popup if the next input message is
2806 * going to hide it anyway.
2808 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2812 msg.hwnd = pmt->hOwnerWnd;
2814 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2815 pmt->trackFlags |= TF_SKIPREMOVE;
2820 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2821 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2823 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2824 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2825 if( msg.message == WM_KEYDOWN &&
2826 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2828 pmt->trackFlags |= TF_SUSPENDPOPUP;
2835 /* failures go through this */
2836 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2840 /***********************************************************************
2843 * Handle a VK_ESCAPE key event in a menu.
2845 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2847 BOOL bEndMenu = TRUE;
2849 if (pmt->hCurrentMenu != pmt->hTopMenu)
2851 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2853 if (menu->wFlags & MF_POPUP)
2855 HMENU hmenutmp, hmenuprev;
2857 hmenuprev = hmenutmp = pmt->hTopMenu;
2859 /* close topmost popup */
2860 while (hmenutmp != pmt->hCurrentMenu)
2862 hmenuprev = hmenutmp;
2863 hmenutmp = MENU_GetSubPopup( hmenuprev );
2866 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2867 pmt->hCurrentMenu = hmenuprev;
2875 /***********************************************************************
2878 * Handle a VK_LEFT key event in a menu.
2880 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2883 HMENU hmenutmp, hmenuprev;
2886 hmenuprev = hmenutmp = pmt->hTopMenu;
2887 menu = MENU_GetMenu( hmenutmp );
2889 /* Try to move 1 column left (if possible) */
2890 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2891 NO_SELECTED_ITEM ) {
2893 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2898 /* close topmost popup */
2899 while (hmenutmp != pmt->hCurrentMenu)
2901 hmenuprev = hmenutmp;
2902 hmenutmp = MENU_GetSubPopup( hmenuprev );
2905 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2906 pmt->hCurrentMenu = hmenuprev;
2908 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2910 /* move menu bar selection if no more popups are left */
2912 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2913 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2915 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2917 /* A sublevel menu was displayed - display the next one
2918 * unless there is another displacement coming up */
2920 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2921 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2922 pmt->hTopMenu, TRUE, wFlags);
2928 /***********************************************************************
2931 * Handle a VK_RIGHT key event in a menu.
2933 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2936 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2939 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2941 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2942 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2944 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2946 /* If already displaying a popup, try to display sub-popup */
2948 hmenutmp = pmt->hCurrentMenu;
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2951 /* if subpopup was displayed then we are done */
2952 if (hmenutmp != pmt->hCurrentMenu) return;
2955 /* Check to see if there's another column */
2956 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2957 NO_SELECTED_ITEM ) {
2958 TRACE("Going to %d.\n", nextcol );
2959 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2964 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2966 if( pmt->hCurrentMenu != pmt->hTopMenu )
2968 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2969 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2970 } else hmenutmp = 0;
2972 /* try to move to the next item */
2973 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2974 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2976 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2977 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2978 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2979 pmt->hTopMenu, TRUE, wFlags);
2983 static void CALLBACK release_capture( BOOL __normal )
2985 set_capture_window( 0, GUI_INMENUMODE, NULL );
2988 /***********************************************************************
2991 * Menu tracking code.
2993 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2994 HWND hwnd, const RECT *lprect )
2999 INT executedMenuId = -1;
3001 BOOL enterIdleSent = FALSE;
3005 mt.hCurrentMenu = hmenu;
3006 mt.hTopMenu = hmenu;
3007 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3011 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3012 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3015 if (!(menu = MENU_GetMenu( hmenu )))
3017 WARN("Invalid menu handle %p\n", hmenu);
3018 SetLastError(ERROR_INVALID_MENU_HANDLE);
3022 if (wFlags & TPM_BUTTONDOWN)
3024 /* Get the result in order to start the tracking or not */
3025 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3026 fEndMenu = !fRemove;
3029 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3031 /* owner may not be visible when tracking a popup, so use the menu itself */
3032 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3033 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3035 __TRY while (!fEndMenu)
3037 menu = MENU_GetMenu( mt.hCurrentMenu );
3038 if (!menu) /* sometimes happens if I do a window manager close */
3041 /* we have to keep the message in the queue until it's
3042 * clear that menu loop is not over yet. */
3046 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3048 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3049 /* remove the message from the queue */
3050 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3056 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3057 enterIdleSent = TRUE;
3058 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3064 /* check if EndMenu() tried to cancel us, by posting this message */
3065 if(msg.message == WM_CANCELMODE)
3067 /* we are now out of the loop */
3070 /* remove the message from the queue */
3071 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3073 /* break out of internal loop, ala ESCAPE */
3077 TranslateMessage( &msg );
3080 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3081 enterIdleSent=FALSE;
3084 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3087 * Use the mouse coordinates in lParam instead of those in the MSG
3088 * struct to properly handle synthetic messages. They are already
3089 * in screen coordinates.
3091 mt.pt.x = (short)LOWORD(msg.lParam);
3092 mt.pt.y = (short)HIWORD(msg.lParam);
3094 /* Find a menu for this mouse event */
3095 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3099 /* no WM_NC... messages in captured state */
3101 case WM_RBUTTONDBLCLK:
3102 case WM_RBUTTONDOWN:
3103 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3105 case WM_LBUTTONDBLCLK:
3106 case WM_LBUTTONDOWN:
3107 /* If the message belongs to the menu, removes it from the queue */
3108 /* Else, end menu tracking */
3109 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3110 fEndMenu = !fRemove;
3114 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3117 /* Check if a menu was selected by the mouse */
3120 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3121 TRACE("executedMenuId %d\n", executedMenuId);
3123 /* End the loop if executedMenuId is an item ID */
3124 /* or if the job was done (executedMenuId = 0). */
3125 fEndMenu = fRemove = (executedMenuId != -1);
3127 /* No menu was selected by the mouse */
3128 /* if the function was called by TrackPopupMenu, continue
3129 with the menu tracking. If not, stop it */
3131 fEndMenu = !(wFlags & TPM_POPUPMENU);
3136 /* the selected menu item must be changed every time */
3137 /* the mouse moves. */
3140 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3142 } /* switch(msg.message) - mouse */
3144 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3146 fRemove = TRUE; /* Keyboard messages are always removed */
3160 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3161 NO_SELECTED_ITEM, FALSE, 0 );
3162 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3163 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3167 case VK_DOWN: /* If on menu bar, pull-down the menu */
3169 menu = MENU_GetMenu( mt.hCurrentMenu );
3170 if (!(menu->wFlags & MF_POPUP))
3171 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3172 else /* otherwise try to move selection */
3173 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3174 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3178 MENU_KeyLeft( &mt, wFlags );
3182 MENU_KeyRight( &mt, wFlags );
3186 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3192 hi.cbSize = sizeof(HELPINFO);
3193 hi.iContextType = HELPINFO_MENUITEM;
3194 if (menu->FocusedItem == NO_SELECTED_ITEM)
3197 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3198 hi.hItemHandle = hmenu;
3199 hi.dwContextId = menu->dwContextHelpID;
3200 hi.MousePos = msg.pt;
3201 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3208 break; /* WM_KEYDOWN */
3215 if (msg.wParam == '\r' || msg.wParam == ' ')
3217 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3218 fEndMenu = (executedMenuId != -2);
3223 /* Hack to avoid control chars. */
3224 /* We will find a better way real soon... */
3225 if (msg.wParam < 32) break;
3227 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3228 LOWORD(msg.wParam), FALSE );
3229 if (pos == (UINT)-2) fEndMenu = TRUE;
3230 else if (pos == (UINT)-1) MessageBeep(0);
3233 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3235 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3236 fEndMenu = (executedMenuId != -2);
3240 } /* switch(msg.message) - kbd */
3244 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3245 DispatchMessageW( &msg );
3249 if (!fEndMenu) fRemove = TRUE;
3251 /* finally remove message from the queue */
3253 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3254 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3255 else mt.trackFlags &= ~TF_SKIPREMOVE;
3257 __FINALLY( release_capture )
3259 /* If dropdown is still painted and the close box is clicked on
3260 then the menu will be destroyed as part of the DispatchMessage above.
3261 This will then invalidate the menu handle in mt.hTopMenu. We should
3262 check for this first. */
3263 if( IsMenu( mt.hTopMenu ) )
3265 menu = MENU_GetMenu( mt.hTopMenu );
3267 if( IsWindow( mt.hOwnerWnd ) )
3269 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3271 if (menu && (menu->wFlags & MF_POPUP))
3273 DestroyWindow( menu->hWnd );
3276 if (!(wFlags & TPM_NONOTIFY))
3277 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3278 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3280 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3281 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3284 /* Reset the variable for hiding menu */
3285 if( menu ) menu->bTimeToHide = FALSE;
3288 /* The return value is only used by TrackPopupMenu */
3289 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3290 if (executedMenuId == -1) executedMenuId = 0;
3291 return executedMenuId;
3294 /***********************************************************************
3297 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3301 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3305 /* This makes the menus of applications built with Delphi work.
3306 * It also enables menus to be displayed in more than one window,
3307 * but there are some bugs left that need to be fixed in this case.
3309 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3310 if (!top_popup) top_popup_hmenu = hMenu;
3312 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3313 if (!(wFlags & TPM_NONOTIFY))
3314 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3316 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3318 if (!(wFlags & TPM_NONOTIFY))
3320 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3321 /* If an app changed/recreated menu bar entries in WM_INITMENU
3322 * menu sizes will be recalculated once the menu created/shown.
3329 /***********************************************************************
3332 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3334 TRACE("hwnd=%p\n", hWnd);
3336 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3339 top_popup_hmenu = NULL;
3343 /***********************************************************************
3344 * MENU_TrackMouseMenuBar
3346 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3348 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3350 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3351 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3353 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3355 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3358 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3359 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3360 MENU_ExitTracking(hWnd, FALSE);
3365 /***********************************************************************
3366 * MENU_TrackKbdMenuBar
3368 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3370 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3372 UINT uItem = NO_SELECTED_ITEM;
3374 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3376 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3378 /* find window that has a menu */
3380 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3381 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3383 /* check if we have to track a system menu */
3385 hTrackMenu = GetMenu( hwnd );
3386 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3388 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3389 hTrackMenu = get_win_sys_menu( hwnd );
3391 wParam |= HTSYSMENU; /* prevent item lookup */
3393 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3395 if (!IsMenu( hTrackMenu )) return;
3397 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3399 if( wChar && wChar != ' ' )
3401 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3402 if ( uItem >= (UINT)(-2) )
3404 if( uItem == (UINT)(-1) ) MessageBeep(0);
3405 /* schedule end of menu tracking */
3406 wFlags |= TF_ENDMENU;
3411 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3413 if (!(wParam & HTSYSMENU) || wChar == ' ')
3415 if( uItem == NO_SELECTED_ITEM )
3416 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3418 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3422 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3423 MENU_ExitTracking( hwnd, FALSE );
3426 /**********************************************************************
3427 * TrackPopupMenuEx (USER32.@)
3429 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3430 HWND hWnd, LPTPMPARAMS lpTpm )
3435 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3436 hMenu, wFlags, x, y, hWnd, lpTpm,
3437 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3439 /* Parameter check */
3440 /* FIXME: this check is performed several times, here and in the called
3441 functions. That could be optimized */
3442 if (!(menu = MENU_GetMenu( hMenu )))
3444 SetLastError( ERROR_INVALID_MENU_HANDLE );
3448 if (IsWindow(menu->hWnd))
3450 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3454 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3456 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3457 if (!(wFlags & TPM_NONOTIFY))
3458 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3460 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3461 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3462 lpTpm ? &lpTpm->rcExclude : NULL );
3463 MENU_ExitTracking(hWnd, TRUE);
3468 /**********************************************************************
3469 * TrackPopupMenu (USER32.@)
3471 * Like the win32 API, the function return the command ID only if the
3472 * flag TPM_RETURNCMD is on.
3475 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3476 INT nReserved, HWND hWnd, const RECT *lpRect )
3478 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3481 /***********************************************************************
3484 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3486 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3488 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3494 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3495 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3499 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3500 return MA_NOACTIVATE;
3505 BeginPaint( hwnd, &ps );
3506 MENU_DrawPopupMenu( hwnd, ps.hdc,
3507 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3508 EndPaint( hwnd, &ps );
3512 case WM_PRINTCLIENT:
3514 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3515 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3523 /* zero out global pointer in case resident popup window was destroyed. */
3524 if (hwnd == top_popup) {
3526 top_popup_hmenu = NULL;
3534 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3537 SetWindowLongPtrW( hwnd, 0, 0 );
3540 case MM_SETMENUHANDLE:
3541 SetWindowLongPtrW( hwnd, 0, wParam );
3544 case MM_GETMENUHANDLE:
3546 return GetWindowLongPtrW( hwnd, 0 );
3549 return DefWindowProcW( hwnd, message, wParam, lParam );
3555 /***********************************************************************
3556 * MENU_GetMenuBarHeight
3558 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3560 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3561 INT orgX, INT orgY )
3567 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3569 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3571 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3572 SelectObject( hdc, get_menu_font(FALSE));
3573 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3574 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3575 ReleaseDC( hwnd, hdc );
3576 return lppop->Height;
3580 /*******************************************************************
3581 * ChangeMenuA (USER32.@)
3583 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3584 UINT id, UINT flags )
3586 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3587 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3589 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3590 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3592 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3593 flags & MF_BYPOSITION ? pos : id,
3594 flags & ~MF_REMOVE );
3595 /* Default: MF_INSERT */
3596 return InsertMenuA( hMenu, pos, flags, id, data );
3600 /*******************************************************************
3601 * ChangeMenuW (USER32.@)
3603 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3604 UINT id, UINT flags )
3606 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3607 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3609 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3610 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3612 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3613 flags & MF_BYPOSITION ? pos : id,
3614 flags & ~MF_REMOVE );
3615 /* Default: MF_INSERT */
3616 return InsertMenuW( hMenu, pos, flags, id, data );
3620 /*******************************************************************
3621 * CheckMenuItem (USER32.@)
3623 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3628 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3629 ret = item->fState & MF_CHECKED;
3630 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3631 else item->fState &= ~MF_CHECKED;
3636 /**********************************************************************
3637 * EnableMenuItem (USER32.@)
3639 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3645 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3647 /* Get the Popupmenu to access the owner menu */
3648 if (!(menu = MENU_GetMenu(hMenu)))
3651 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3654 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3655 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3657 /* If the close item in the system menu change update the close button */
3658 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3660 if (menu->hSysMenuOwner != 0)
3663 POPUPMENU* parentMenu;
3665 /* Get the parent menu to access*/
3666 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3669 /* Refresh the frame to reflect the change */
3670 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3672 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3680 /*******************************************************************
3681 * GetMenuStringA (USER32.@)
3683 INT WINAPI GetMenuStringA(
3684 HMENU hMenu, /* [in] menuhandle */
3685 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3686 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3687 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3688 UINT wFlags /* [in] MF_ flags */
3692 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3693 if (str && nMaxSiz) str[0] = '\0';
3694 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3695 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3698 if (!item->text) return 0;
3699 if (!str || !nMaxSiz) return strlenW(item->text);
3700 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3702 TRACE("returning %s\n", debugstr_a(str));
3707 /*******************************************************************
3708 * GetMenuStringW (USER32.@)
3710 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3711 LPWSTR str, INT nMaxSiz, UINT wFlags )
3715 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3716 if (str && nMaxSiz) str[0] = '\0';
3717 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3718 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3721 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3722 if( !(item->text)) {
3726 lstrcpynW( str, item->text, nMaxSiz );
3727 TRACE("returning %s\n", debugstr_w(str));
3728 return strlenW(str);
3732 /**********************************************************************
3733 * HiliteMenuItem (USER32.@)
3735 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3739 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3740 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3741 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3742 if (menu->FocusedItem == wItemID) return TRUE;
3743 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3744 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3749 /**********************************************************************
3750 * GetMenuState (USER32.@)
3752 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3755 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3756 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3757 debug_print_menuitem (" item: ", item, "");
3758 if (item->fType & MF_POPUP)
3760 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3761 if (!menu) return -1;
3762 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3766 /* We used to (from way back then) mask the result to 0xff. */
3767 /* I don't know why and it seems wrong as the documented */
3768 /* return flag MF_SEPARATOR is outside that mask. */
3769 return (item->fType | item->fState);
3774 /**********************************************************************
3775 * GetMenuItemCount (USER32.@)
3777 INT WINAPI GetMenuItemCount( HMENU hMenu )
3779 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3780 if (!menu) return -1;
3781 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3782 return menu->nItems;
3786 /**********************************************************************
3787 * GetMenuItemID (USER32.@)
3789 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3793 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3794 if (lpmi->fType & MF_POPUP) return -1;
3800 /**********************************************************************
3803 * Uses flags, id and text ptr, passed by InsertMenu() and
3804 * ModifyMenu() to setup a MenuItemInfo structure.
3806 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3807 LPMENUITEMINFOW pmii)
3809 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3810 pmii->cbSize = sizeof( MENUITEMINFOW);
3811 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3812 /* setting bitmap clears text and vice versa */
3813 if( IS_STRING_ITEM(flags)) {
3814 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3816 flags |= MF_SEPARATOR;
3817 /* Item beginning with a backspace is a help item */
3818 /* FIXME: wrong place, this is only true in win16 */
3819 else if( *str == '\b') {
3823 pmii->dwTypeData = (LPWSTR)str;
3824 } else if( flags & MFT_BITMAP){
3825 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3826 pmii->hbmpItem = (HBITMAP)str;
3828 if( flags & MF_OWNERDRAW){
3829 pmii->fMask |= MIIM_DATA;
3830 pmii->dwItemData = (ULONG_PTR) str;
3832 if( flags & MF_POPUP) {
3833 pmii->fMask |= MIIM_SUBMENU;
3834 pmii->hSubMenu = (HMENU)id;
3836 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3837 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3838 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3839 pmii->wID = (UINT)id;
3843 /*******************************************************************
3844 * InsertMenuW (USER32.@)
3846 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3847 UINT_PTR id, LPCWSTR str )
3852 if (IS_STRING_ITEM(flags) && str)
3853 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3854 hMenu, pos, flags, id, debugstr_w(str) );
3855 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3856 hMenu, pos, flags, id, str );
3858 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3859 MENU_mnu2mnuii( flags, id, str, &mii);
3860 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3862 RemoveMenu( hMenu, pos, flags );
3866 item->hCheckBit = item->hUnCheckBit = 0;
3871 /*******************************************************************
3872 * InsertMenuA (USER32.@)
3874 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3875 UINT_PTR id, LPCSTR str )
3879 if (IS_STRING_ITEM(flags) && str)
3881 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3882 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3885 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3886 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3887 HeapFree( GetProcessHeap(), 0, newstr );
3891 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3895 /*******************************************************************
3896 * AppendMenuA (USER32.@)
3898 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3899 UINT_PTR id, LPCSTR data )
3901 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3905 /*******************************************************************
3906 * AppendMenuW (USER32.@)
3908 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3909 UINT_PTR id, LPCWSTR data )
3911 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3915 /**********************************************************************
3916 * RemoveMenu (USER32.@)
3918 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3923 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3924 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3925 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3929 MENU_FreeItemData( item );
3931 if (--menu->nItems == 0)
3933 HeapFree( GetProcessHeap(), 0, menu->items );
3938 while(nPos < menu->nItems)
3944 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3945 menu->nItems * sizeof(MENUITEM) );
3951 /**********************************************************************
3952 * DeleteMenu (USER32.@)
3954 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3956 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3957 if (!item) return FALSE;
3958 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3959 /* nPos is now the position of the item */
3960 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3965 /*******************************************************************
3966 * ModifyMenuW (USER32.@)
3968 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3969 UINT_PTR id, LPCWSTR str )
3974 if (IS_STRING_ITEM(flags))
3975 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3977 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3979 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3980 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3981 MENU_mnu2mnuii( flags, id, str, &mii);
3982 return SetMenuItemInfo_common( item, &mii, TRUE);
3986 /*******************************************************************
3987 * ModifyMenuA (USER32.@)
3989 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3990 UINT_PTR id, LPCSTR str )
3994 if (IS_STRING_ITEM(flags) && str)
3996 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3997 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4000 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4001 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4002 HeapFree( GetProcessHeap(), 0, newstr );
4006 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4010 /**********************************************************************
4011 * CreatePopupMenu (USER32.@)
4013 HMENU WINAPI CreatePopupMenu(void)
4018 if (!(hmenu = CreateMenu())) return 0;
4019 menu = MENU_GetMenu( hmenu );
4020 menu->wFlags |= MF_POPUP;
4021 menu->bTimeToHide = FALSE;
4026 /**********************************************************************
4027 * GetMenuCheckMarkDimensions (USER.417)
4028 * GetMenuCheckMarkDimensions (USER32.@)
4030 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4032 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4036 /**********************************************************************
4037 * SetMenuItemBitmaps (USER32.@)
4039 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4040 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4044 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4046 if (!hNewCheck && !hNewUnCheck)
4048 item->fState &= ~MF_USECHECKBITMAPS;
4050 else /* Install new bitmaps */
4052 item->hCheckBit = hNewCheck;
4053 item->hUnCheckBit = hNewUnCheck;
4054 item->fState |= MF_USECHECKBITMAPS;
4060 /**********************************************************************
4061 * CreateMenu (USER32.@)
4063 HMENU WINAPI CreateMenu(void)
4068 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4069 menu->FocusedItem = NO_SELECTED_ITEM;
4070 menu->bTimeToHide = FALSE;
4072 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4074 TRACE("return %p\n", hMenu );
4080 /**********************************************************************
4081 * DestroyMenu (USER32.@)
4083 BOOL WINAPI DestroyMenu( HMENU hMenu )
4087 TRACE("(%p)\n", hMenu);
4089 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4090 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4092 /* DestroyMenu should not destroy system menu popup owner */
4093 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4095 DestroyWindow( lppop->hWnd );
4099 if (lppop->items) /* recursively destroy submenus */
4102 MENUITEM *item = lppop->items;
4103 for (i = lppop->nItems; i > 0; i--, item++)
4105 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4106 MENU_FreeItemData( item );
4108 HeapFree( GetProcessHeap(), 0, lppop->items );
4110 HeapFree( GetProcessHeap(), 0, lppop );
4115 /**********************************************************************
4116 * GetSystemMenu (USER32.@)
4118 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4120 WND *wndPtr = WIN_GetPtr( hWnd );
4123 if (wndPtr == WND_DESKTOP) return 0;
4124 if (wndPtr == WND_OTHER_PROCESS)
4126 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4130 if (wndPtr->hSysMenu && bRevert)
4132 DestroyMenu(wndPtr->hSysMenu);
4133 wndPtr->hSysMenu = 0;
4136 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4137 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4139 if( wndPtr->hSysMenu )
4142 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4144 /* Store the dummy sysmenu handle to facilitate the refresh */
4145 /* of the close button if the SC_CLOSE item change */
4146 menu = MENU_GetMenu(retvalue);
4148 menu->hSysMenuOwner = wndPtr->hSysMenu;
4150 WIN_ReleasePtr( wndPtr );
4152 return bRevert ? 0 : retvalue;
4156 /*******************************************************************
4157 * SetSystemMenu (USER32.@)
4159 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4161 WND *wndPtr = WIN_GetPtr( hwnd );
4163 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4165 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4166 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4167 WIN_ReleasePtr( wndPtr );
4174 /**********************************************************************
4175 * GetMenu (USER32.@)
4177 HMENU WINAPI GetMenu( HWND hWnd )
4179 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4180 TRACE("for %p returning %p\n", hWnd, retvalue);
4184 /**********************************************************************
4185 * GetMenuBarInfo (USER32.@)
4187 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4193 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4198 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4201 if (class_atom != POPUPMENU_CLASS_ATOM)
4203 WARN("called on invalid window: %d\n", class_atom);
4204 SetLastError(ERROR_INVALID_MENU_HANDLE);
4208 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4211 hmenu = GetMenu(hwnd);
4214 hmenu = GetSystemMenu(hwnd, FALSE);
4223 if (pmbi->cbSize != sizeof(MENUBARINFO))
4225 SetLastError(ERROR_INVALID_PARAMETER);
4229 menu = MENU_GetMenu(hmenu);
4232 if (idItem < 0 || idItem > menu->nItems)
4237 SetRectEmpty(&pmbi->rcBar);
4239 else if (idItem == 0)
4241 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4242 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4243 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4247 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4250 pmbi->hMenu = hmenu;
4251 pmbi->hwndMenu = NULL;
4252 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4255 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4256 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4258 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4260 pmbi->hwndMenu = menu->hWnd;
4265 pmbi->fFocused = pmbi->fBarFocused;
4271 /**********************************************************************
4274 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4275 * SetWindowPos call that would result if SetMenu were called directly.
4277 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4279 TRACE("(%p, %p);\n", hWnd, hMenu);
4281 if (hMenu && !IsMenu(hMenu))
4283 WARN("hMenu %p is not a menu handle\n", hMenu);
4286 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4289 hWnd = WIN_GetFullHandle( hWnd );
4290 if (GetCapture() == hWnd)
4291 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4297 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4299 lpmenu->hWnd = hWnd;
4300 lpmenu->Height = 0; /* Make sure we recalculate the size */
4302 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4307 /**********************************************************************
4308 * SetMenu (USER32.@)
4310 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4312 if(!MENU_SetMenu(hWnd, hMenu))
4315 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4316 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4321 /**********************************************************************
4322 * GetSubMenu (USER32.@)
4324 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4328 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4329 if (!(lpmi->fType & MF_POPUP)) return 0;
4330 return lpmi->hSubMenu;
4334 /**********************************************************************
4335 * DrawMenuBar (USER32.@)
4337 BOOL WINAPI DrawMenuBar( HWND hWnd )
4340 HMENU hMenu = GetMenu(hWnd);
4342 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4344 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4346 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4347 lppop->hwndOwner = hWnd;
4348 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4349 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4353 /***********************************************************************
4354 * DrawMenuBarTemp (USER32.@)
4358 * called by W98SE desk.cpl Control Panel Applet
4360 * Not 100% sure about the param names, but close.
4362 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4367 BOOL flat_menu = FALSE;
4369 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4372 hMenu = GetMenu(hwnd);
4375 hFont = get_menu_font(FALSE);
4377 lppop = MENU_GetMenu( hMenu );
4378 if (lppop == NULL || lprect == NULL)
4380 retvalue = GetSystemMetrics(SM_CYMENU);
4384 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4386 hfontOld = SelectObject( hDC, hFont);
4388 if (lppop->Height == 0)
4389 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4391 lprect->bottom = lprect->top + lppop->Height;
4393 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4395 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4396 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4397 LineTo( hDC, lprect->right, lprect->bottom );
4399 if (lppop->nItems == 0)
4401 retvalue = GetSystemMetrics(SM_CYMENU);
4405 for (i = 0; i < lppop->nItems; i++)
4407 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4408 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4410 retvalue = lppop->Height;
4413 if (hfontOld) SelectObject (hDC, hfontOld);
4417 /***********************************************************************
4418 * EndMenu (USER.187)
4419 * EndMenu (USER32.@)
4421 BOOL WINAPI EndMenu(void)
4423 /* if we are in the menu code, and it is active */
4424 if (!fEndMenu && top_popup)
4426 /* terminate the menu handling code */
4429 /* needs to be posted to wakeup the internal menu handler */
4430 /* which will now terminate the menu, in the event that */
4431 /* the main window was minimized, or lost focus, so we */
4432 /* don't end up with an orphaned menu */
4433 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4439 /*****************************************************************
4440 * LoadMenuA (USER32.@)
4442 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4444 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4445 if (!hrsrc) return 0;
4446 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4450 /*****************************************************************
4451 * LoadMenuW (USER32.@)
4453 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4455 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4456 if (!hrsrc) return 0;
4457 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4461 /**********************************************************************
4462 * LoadMenuIndirectW (USER32.@)
4464 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4467 WORD version, offset;
4468 LPCSTR p = template;
4470 version = GET_WORD(p);
4472 TRACE("%p, ver %d\n", template, version );
4475 case 0: /* standard format is version of 0 */
4476 offset = GET_WORD(p);
4477 p += sizeof(WORD) + offset;
4478 if (!(hMenu = CreateMenu())) return 0;
4479 if (!MENU_ParseResource( p, hMenu ))
4481 DestroyMenu( hMenu );
4485 case 1: /* extended format is version of 1 */
4486 offset = GET_WORD(p);
4487 p += sizeof(WORD) + offset;
4488 if (!(hMenu = CreateMenu())) return 0;
4489 if (!MENUEX_ParseResource( p, hMenu))
4491 DestroyMenu( hMenu );
4496 ERR("version %d not supported.\n", version);
4502 /**********************************************************************
4503 * LoadMenuIndirectA (USER32.@)
4505 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4507 return LoadMenuIndirectW( template );
4511 /**********************************************************************
4514 BOOL WINAPI IsMenu(HMENU hmenu)
4516 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4520 SetLastError(ERROR_INVALID_MENU_HANDLE);
4526 /**********************************************************************
4527 * GetMenuItemInfo_common
4530 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4531 LPMENUITEMINFOW lpmii, BOOL unicode)
4533 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4535 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4538 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4542 if( lpmii->fMask & MIIM_TYPE) {
4543 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4544 WARN("invalid combination of fMask bits used\n");
4545 /* this does not happen on Win9x/ME */
4546 SetLastError( ERROR_INVALID_PARAMETER);
4549 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4550 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4551 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4552 if( lpmii->fType & MFT_BITMAP) {
4553 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4555 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4556 /* this does not happen on Win9x/ME */
4557 lpmii->dwTypeData = 0;
4562 /* copy the text string */
4563 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4565 if(lpmii->dwTypeData && lpmii->cch) {
4568 *((WCHAR *)lpmii->dwTypeData) = 0;
4570 *((CHAR *)lpmii->dwTypeData) = 0;
4576 len = strlenW(menu->text);
4577 if(lpmii->dwTypeData && lpmii->cch)
4578 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4582 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4583 0, NULL, NULL ) - 1;
4584 if(lpmii->dwTypeData && lpmii->cch)
4585 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4586 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4587 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4589 /* if we've copied a substring we return its length */
4590 if(lpmii->dwTypeData && lpmii->cch)
4591 if (lpmii->cch <= len + 1)
4596 /* return length of string */
4597 /* not on Win9x/ME if fType & MFT_BITMAP */
4603 if (lpmii->fMask & MIIM_FTYPE)
4604 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4606 if (lpmii->fMask & MIIM_BITMAP)
4607 lpmii->hbmpItem = menu->hbmpItem;
4609 if (lpmii->fMask & MIIM_STATE)
4610 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4612 if (lpmii->fMask & MIIM_ID)
4613 lpmii->wID = menu->wID;
4615 if (lpmii->fMask & MIIM_SUBMENU)
4616 lpmii->hSubMenu = menu->hSubMenu;
4618 /* hSubMenu is always cleared
4619 * (not on Win9x/ME ) */
4620 lpmii->hSubMenu = 0;
4623 if (lpmii->fMask & MIIM_CHECKMARKS) {
4624 lpmii->hbmpChecked = menu->hCheckBit;
4625 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4627 if (lpmii->fMask & MIIM_DATA)
4628 lpmii->dwItemData = menu->dwItemData;
4633 /**********************************************************************
4634 * GetMenuItemInfoA (USER32.@)
4636 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4637 LPMENUITEMINFOA lpmii)
4641 if( lpmii->cbSize != sizeof( mii) &&
4642 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4643 SetLastError( ERROR_INVALID_PARAMETER);
4646 memcpy( &mii, lpmii, lpmii->cbSize);
4647 mii.cbSize = sizeof( mii);
4648 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4649 (LPMENUITEMINFOW)&mii, FALSE);
4650 mii.cbSize = lpmii->cbSize;
4651 memcpy( lpmii, &mii, mii.cbSize);
4655 /**********************************************************************
4656 * GetMenuItemInfoW (USER32.@)
4658 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4659 LPMENUITEMINFOW lpmii)
4663 if( lpmii->cbSize != sizeof( mii) &&
4664 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4665 SetLastError( ERROR_INVALID_PARAMETER);
4668 memcpy( &mii, lpmii, lpmii->cbSize);
4669 mii.cbSize = sizeof( mii);
4670 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4671 mii.cbSize = lpmii->cbSize;
4672 memcpy( lpmii, &mii, mii.cbSize);
4677 /* set a menu item text from a ASCII or Unicode string */
4678 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4684 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4685 strcpyW( menu->text, text );
4689 LPCSTR str = (LPCSTR)text;
4690 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4691 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4692 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4697 /**********************************************************************
4700 * detect if there are loops in the menu tree (or the depth is too large)
4702 static int MENU_depth( POPUPMENU *pmenu, int depth)
4709 if( depth > MAXMENUDEPTH) return depth;
4710 item = pmenu->items;
4712 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4713 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4715 int bdepth = MENU_depth( psubmenu, depth);
4716 if( bdepth > subdepth) subdepth = bdepth;
4718 if( subdepth > MAXMENUDEPTH)
4719 TRACE("<- hmenu %p\n", item->hSubMenu);
4725 /**********************************************************************
4726 * SetMenuItemInfo_common
4728 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4729 * MIIM_BITMAP and MIIM_STRING flags instead.
4732 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4733 const MENUITEMINFOW *lpmii,
4736 if (!menu) return FALSE;
4738 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4740 if (lpmii->fMask & MIIM_FTYPE ) {
4741 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4742 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4744 if (lpmii->fMask & MIIM_STRING ) {
4745 /* free the string when used */
4746 HeapFree(GetProcessHeap(), 0, menu->text);
4747 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4750 if (lpmii->fMask & MIIM_STATE)
4751 /* Other menu items having MFS_DEFAULT are not converted
4753 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4755 if (lpmii->fMask & MIIM_ID)
4756 menu->wID = lpmii->wID;
4758 if (lpmii->fMask & MIIM_SUBMENU) {
4759 menu->hSubMenu = lpmii->hSubMenu;
4760 if (menu->hSubMenu) {
4761 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4763 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4764 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4768 subMenu->wFlags |= MF_POPUP;
4769 menu->fType |= MF_POPUP;
4771 SetLastError( ERROR_INVALID_PARAMETER);
4776 menu->fType &= ~MF_POPUP;
4779 if (lpmii->fMask & MIIM_CHECKMARKS)
4781 menu->hCheckBit = lpmii->hbmpChecked;
4782 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4784 if (lpmii->fMask & MIIM_DATA)
4785 menu->dwItemData = lpmii->dwItemData;
4787 if (lpmii->fMask & MIIM_BITMAP)
4788 menu->hbmpItem = lpmii->hbmpItem;
4790 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4791 menu->fType |= MFT_SEPARATOR;
4793 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4797 /**********************************************************************
4798 * MENU_NormalizeMenuItemInfoStruct
4800 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4801 * check, copy and extend the MENUITEMINFO struct from the version that the application
4802 * supplied to the version used by wine source. */
4803 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4804 MENUITEMINFOW *pmii_out )
4806 /* do we recognize the size? */
4807 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4808 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4809 SetLastError( ERROR_INVALID_PARAMETER);
4812 /* copy the fields that we have */
4813 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4814 /* if the hbmpItem member is missing then extend */
4815 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4816 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4817 pmii_out->hbmpItem = NULL;
4819 /* test for invalid bit combinations */
4820 if( (pmii_out->fMask & MIIM_TYPE &&
4821 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4822 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4823 WARN("invalid combination of fMask bits used\n");
4824 /* this does not happen on Win9x/ME */
4825 SetLastError( ERROR_INVALID_PARAMETER);
4828 /* convert old style (MIIM_TYPE) to the new */
4829 if( pmii_out->fMask & MIIM_TYPE){
4830 pmii_out->fMask |= MIIM_FTYPE;
4831 if( IS_STRING_ITEM(pmii_out->fType)){
4832 pmii_out->fMask |= MIIM_STRING;
4833 } else if( (pmii_out->fType) & MFT_BITMAP){
4834 pmii_out->fMask |= MIIM_BITMAP;
4835 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4841 /**********************************************************************
4842 * SetMenuItemInfoA (USER32.@)
4844 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4845 const MENUITEMINFOA *lpmii)
4849 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4851 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4853 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4857 /**********************************************************************
4858 * SetMenuItemInfoW (USER32.@)
4860 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4861 const MENUITEMINFOW *lpmii)
4865 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4867 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4868 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4869 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4872 /**********************************************************************
4873 * SetMenuDefaultItem (USER32.@)
4876 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4882 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4884 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4886 /* reset all default-item flags */
4888 for (i = 0; i < menu->nItems; i++, item++)
4890 item->fState &= ~MFS_DEFAULT;
4893 /* no default item */
4902 if ( uItem >= menu->nItems ) return FALSE;
4903 item[uItem].fState |= MFS_DEFAULT;
4908 for (i = 0; i < menu->nItems; i++, item++)
4910 if (item->wID == uItem)
4912 item->fState |= MFS_DEFAULT;
4921 /**********************************************************************
4922 * GetMenuDefaultItem (USER32.@)
4924 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4930 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4932 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4934 /* find default item */
4938 if (! item) return -1;
4940 while ( !( item->fState & MFS_DEFAULT ) )
4943 if (i >= menu->nItems ) return -1;
4946 /* default: don't return disabled items */
4947 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4949 /* search rekursiv when needed */
4950 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4953 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4954 if ( -1 != ret ) return ret;
4956 /* when item not found in submenu, return the popup item */
4958 return ( bypos ) ? i : item->wID;
4963 /**********************************************************************
4964 * InsertMenuItemA (USER32.@)
4966 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4967 const MENUITEMINFOA *lpmii)
4972 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4974 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4976 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4977 return SetMenuItemInfo_common(item, &mii, FALSE);
4981 /**********************************************************************
4982 * InsertMenuItemW (USER32.@)
4984 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4985 const MENUITEMINFOW *lpmii)
4990 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4992 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4994 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4995 return SetMenuItemInfo_common(item, &mii, TRUE);
4998 /**********************************************************************
4999 * CheckMenuRadioItem (USER32.@)
5002 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5003 UINT first, UINT last, UINT check,
5008 MENUITEM *mi_first = NULL, *mi_check;
5009 HMENU m_first, m_check;
5011 for (i = first; i <= last; i++)
5018 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5019 if (!mi_first) continue;
5020 mi_check = mi_first;
5026 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5027 if (!mi_check) continue;
5030 if (m_first != m_check) continue;
5031 if (mi_check->fType == MFT_SEPARATOR) continue;
5035 mi_check->fType |= MFT_RADIOCHECK;
5036 mi_check->fState |= MFS_CHECKED;
5041 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5042 mi_check->fState &= ~MFS_CHECKED;
5050 /**********************************************************************
5051 * GetMenuItemRect (USER32.@)
5053 * ATTENTION: Here, the returned values in rect are the screen
5054 * coordinates of the item just like if the menu was
5055 * always on the upper left side of the application.
5058 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5061 POPUPMENU *itemMenu;
5065 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5067 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5068 referenceHwnd = hwnd;
5072 itemMenu = MENU_GetMenu(hMenu);
5073 if (itemMenu == NULL)
5076 if(itemMenu->hWnd == 0)
5078 referenceHwnd = itemMenu->hWnd;
5081 if ((rect == NULL) || (item == NULL))
5086 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5091 /**********************************************************************
5092 * SetMenuInfo (USER32.@)
5095 * actually use the items to draw the menu
5096 * (recalculate and/or redraw)
5098 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5101 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5103 if (lpmi->fMask & MIM_BACKGROUND)
5104 menu->hbrBack = lpmi->hbrBack;
5106 if (lpmi->fMask & MIM_HELPID)
5107 menu->dwContextHelpID = lpmi->dwContextHelpID;
5109 if (lpmi->fMask & MIM_MAXHEIGHT)
5110 menu->cyMax = lpmi->cyMax;
5112 if (lpmi->fMask & MIM_MENUDATA)
5113 menu->dwMenuData = lpmi->dwMenuData;
5115 if (lpmi->fMask & MIM_STYLE)
5116 menu->dwStyle = lpmi->dwStyle;
5118 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5120 MENUITEM *item = menu->items;
5121 for( i = menu->nItems; i; i--, item++)
5122 if( item->fType & MF_POPUP)
5123 menu_SetMenuInfo( item->hSubMenu, lpmi);
5128 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5130 TRACE("(%p %p)\n", hMenu, lpmi);
5131 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5132 if( lpmi->fMask & MIM_STYLE) {
5133 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5134 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5135 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5139 SetLastError( ERROR_INVALID_PARAMETER);
5143 /**********************************************************************
5144 * GetMenuInfo (USER32.@)
5150 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5153 TRACE("(%p %p)\n", hMenu, lpmi);
5155 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5158 if (lpmi->fMask & MIM_BACKGROUND)
5159 lpmi->hbrBack = menu->hbrBack;
5161 if (lpmi->fMask & MIM_HELPID)
5162 lpmi->dwContextHelpID = menu->dwContextHelpID;
5164 if (lpmi->fMask & MIM_MAXHEIGHT)
5165 lpmi->cyMax = menu->cyMax;
5167 if (lpmi->fMask & MIM_MENUDATA)
5168 lpmi->dwMenuData = menu->dwMenuData;
5170 if (lpmi->fMask & MIM_STYLE)
5171 lpmi->dwStyle = menu->dwStyle;
5175 SetLastError( ERROR_INVALID_PARAMETER);
5180 /**********************************************************************
5181 * SetMenuContextHelpId (USER32.@)
5183 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5187 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5189 if ((menu = MENU_GetMenu(hMenu)))
5191 menu->dwContextHelpID = dwContextHelpID;
5198 /**********************************************************************
5199 * GetMenuContextHelpId (USER32.@)
5201 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5205 TRACE("(%p)\n", hMenu);
5207 if ((menu = MENU_GetMenu(hMenu)))
5209 return menu->dwContextHelpID;
5214 /**********************************************************************
5215 * MenuItemFromPoint (USER32.@)
5217 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5219 POPUPMENU *menu = MENU_GetMenu(hMenu);
5222 /*FIXME: Do we have to handle hWnd here? */
5223 if (!menu) return -1;
5224 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5229 /**********************************************************************
5230 * translate_accelerator
5232 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5233 BYTE fVirt, WORD key, WORD cmd )
5238 if (wParam != key) return FALSE;
5240 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5241 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5242 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5244 if (message == WM_CHAR || message == WM_SYSCHAR)
5246 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5248 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5254 if(fVirt & FVIRTKEY)
5256 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5257 wParam, 0xff & HIWORD(lParam));
5259 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5260 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5264 if (!(lParam & 0x01000000)) /* no special_key */
5266 if ((fVirt & FALT) && (lParam & 0x20000000))
5267 { /* ^^ ALT pressed */
5268 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5277 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5281 HMENU hMenu, hSubMenu, hSysMenu;
5282 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5284 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5285 hSysMenu = get_win_sys_menu( hWnd );
5287 /* find menu item and ask application to initialize it */
5288 /* 1. in the system menu */
5289 hSubMenu = hSysMenu;
5291 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5295 if (!IsWindowEnabled(hWnd))
5299 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5300 if(hSubMenu != hSysMenu)
5302 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5303 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5304 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5306 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5309 else /* 2. in the window's menu */
5313 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5317 if (!IsWindowEnabled(hWnd))
5321 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5322 if(hSubMenu != hMenu)
5324 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5325 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5326 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5328 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5335 if (uSysStat != (UINT)-1)
5337 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5344 if (uStat != (UINT)-1)
5350 if (uStat & (MF_DISABLED|MF_GRAYED))
5362 if( mesg==WM_COMMAND )
5364 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5365 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5367 else if( mesg==WM_SYSCOMMAND )
5369 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5370 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5374 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5375 * #0: unknown (please report!)
5376 * #1: for WM_KEYUP,WM_SYSKEYUP
5377 * #2: mouse is captured
5378 * #3: window is disabled
5379 * #4: it's a disabled system menu option
5380 * #5: it's a menu option, but window is iconic
5381 * #6: it's a menu option, but disabled
5383 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5385 ERR_(accel)(" unknown reason - please report!\n");
5390 /**********************************************************************
5391 * TranslateAcceleratorA (USER32.@)
5392 * TranslateAccelerator (USER32.@)
5394 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5396 switch (msg->message)
5400 return TranslateAcceleratorW( hWnd, hAccel, msg );
5406 char ch = LOWORD(msg->wParam);
5408 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5409 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5410 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5418 /**********************************************************************
5419 * TranslateAcceleratorW (USER32.@)
5421 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5423 ACCEL data[32], *ptr = data;
5426 if (!hWnd) return 0;
5428 if (msg->message != WM_KEYDOWN &&
5429 msg->message != WM_SYSKEYDOWN &&
5430 msg->message != WM_CHAR &&
5431 msg->message != WM_SYSCHAR)
5434 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5435 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5437 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5438 if (count > sizeof(data)/sizeof(data[0]))
5440 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5442 count = CopyAcceleratorTableW( hAccel, ptr, count );
5443 for (i = 0; i < count; i++)
5445 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5446 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5449 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );