4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
92 /* Popup menu structure */
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
132 #define MENU_MAGIC 0x554d /* 'MU' */
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
231 TRACE( ", Sub=%p", mp->hSubMenu);
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 TRACE( "+0x%x", flags);
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
260 TRACE( "+0x%x", flags);
263 TRACE( ", Chk=%p", mp->hCheckBit);
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
267 TRACE( ", Text=%s", debugstr_w(mp->text));
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
280 TRACE(" %s\n", postfix);
287 /***********************************************************************
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
303 /***********************************************************************
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
315 WIN_ReleasePtr( win );
320 /***********************************************************************
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
331 NONCLIENTMETRICSW ncm;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
347 /* another thread beat us to it */
355 /***********************************************************************
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
410 /***********************************************************************
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
434 /**********************************************************************
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
456 hPopupMenu = MENU_CopySysPopup();
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
569 TRACE("ret %d.\n", i );
576 /***********************************************************************
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
616 else if (item->wID == *nPos)
625 *nPos = fallback_pos;
630 /***********************************************************************
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
666 static void MENU_FreeItemData( MENUITEM* item )
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_width, arrow_bitmap_height;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_width = bmp.bmWidth;
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
708 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
712 for (i = 0; i < menu->nItems; i++, item++)
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if (PtInRect(&rect, pt))
726 /***********************************************************************
729 * Find the menu item selected by a key press.
730 * Return item id, -1 if none, -2 if we should close the menu.
732 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
733 WCHAR key, BOOL forceMenuChar )
735 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
737 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
741 POPUPMENU *menu = MENU_GetMenu( hmenu );
742 MENUITEM *item = menu->items;
749 for (i = 0; i < menu->nItems; i++, item++)
753 WCHAR *p = item->text - 2;
756 p = strchrW (p + 2, '&');
758 while (p != NULL && p [1] == '&');
759 if (p && (toupperW(p[1]) == toupperW(key))) return i;
763 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
764 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
765 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
766 if (HIWORD(menuchar) == 1) return (UINT)(-2);
772 /***********************************************************************
773 * MENU_GetBitmapItemSize
775 * Get the size of a bitmap item.
777 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
781 HBITMAP bmp = lpitem->hbmpItem;
783 size->cx = size->cy = 0;
785 /* check if there is a magic menu item associated with this item */
786 switch( (INT_PTR) bmp )
788 case (INT_PTR)HBMMENU_CALLBACK:
790 MEASUREITEMSTRUCT measItem;
791 measItem.CtlType = ODT_MENU;
793 measItem.itemID = lpitem->wID;
794 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
795 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
796 measItem.itemData = lpitem->dwItemData;
797 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
798 size->cx = measItem.itemWidth;
799 size->cy = measItem.itemHeight;
803 case (INT_PTR)HBMMENU_SYSTEM:
804 if (lpitem->dwItemData)
806 bmp = (HBITMAP)lpitem->dwItemData;
810 case (INT_PTR)HBMMENU_MBAR_RESTORE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
815 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
818 case (INT_PTR)HBMMENU_POPUP_CLOSE:
819 case (INT_PTR)HBMMENU_POPUP_RESTORE:
820 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
821 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
822 FIXME("Magic %p not implemented\n", bmp );
825 if (GetObjectW(bmp, sizeof(bm), &bm ))
827 size->cx = bm.bmWidth;
828 size->cy = bm.bmHeight;
832 /***********************************************************************
833 * MENU_DrawBitmapItem
835 * Draw a bitmap item.
837 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
838 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
844 int w = rect->right - rect->left;
845 int h = rect->bottom - rect->top;
848 HBITMAP hbmToDraw = lpitem->hbmpItem;
851 /* Check if there is a magic menu item associated with this item */
852 if (IS_MAGIC_BITMAP(hbmToDraw))
857 switch((INT_PTR)hbmToDraw)
859 case (INT_PTR)HBMMENU_SYSTEM:
860 if (lpitem->dwItemData)
862 bmp = (HBITMAP)lpitem->dwItemData;
863 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
867 static HBITMAP hBmpSysMenu;
869 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
871 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
872 /* only use right half of the bitmap */
873 bmp_xoffset = bm.bmWidth / 2;
874 bm.bmWidth -= bmp_xoffset;
877 case (INT_PTR)HBMMENU_MBAR_RESTORE:
878 flags = DFCS_CAPTIONRESTORE;
880 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
881 flags = DFCS_CAPTIONMIN;
883 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
884 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
886 case (INT_PTR)HBMMENU_MBAR_CLOSE:
887 flags = DFCS_CAPTIONCLOSE;
889 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
890 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
892 case (INT_PTR)HBMMENU_CALLBACK:
894 DRAWITEMSTRUCT drawItem;
895 drawItem.CtlType = ODT_MENU;
897 drawItem.itemID = lpitem->wID;
898 drawItem.itemAction = odaction;
899 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
900 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
901 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
904 drawItem.hwndItem = (HWND)hmenu;
906 drawItem.itemData = lpitem->dwItemData;
907 drawItem.rcItem = *rect;
908 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
912 case (INT_PTR)HBMMENU_POPUP_CLOSE:
913 case (INT_PTR)HBMMENU_POPUP_RESTORE:
914 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
915 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
917 FIXME("Magic %p not implemented\n", hbmToDraw);
921 InflateRect( &r, -1, -1 );
922 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
923 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
927 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
930 hdcMem = CreateCompatibleDC( hdc );
931 SelectObject( hdcMem, bmp );
933 /* handle fontsize > bitmap_height */
934 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
936 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
937 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
938 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
939 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
944 /***********************************************************************
947 * Calculate the size of the menu item and store it in lpitem->rect.
949 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
950 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
953 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
954 UINT arrow_bitmap_width;
958 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
959 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
960 (menuBar ? " (MenuBar)" : ""));
962 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
963 arrow_bitmap_width = bm.bmWidth;
965 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
966 if( !menucharsize.cx ) {
967 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
968 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
969 * but it is unlikely an application will depend on that */
970 ODitemheight = HIWORD( GetDialogBaseUnits());
973 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
975 if (lpitem->fType & MF_OWNERDRAW)
977 MEASUREITEMSTRUCT mis;
978 mis.CtlType = ODT_MENU;
980 mis.itemID = lpitem->wID;
981 mis.itemData = lpitem->dwItemData;
982 mis.itemHeight = ODitemheight;
984 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
985 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
986 * width of a menufont character to the width of an owner-drawn menu.
988 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
990 /* under at least win95 you seem to be given a standard
991 height for the menu and the height value is ignored */
992 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
994 lpitem->rect.bottom += mis.itemHeight;
996 TRACE("id=%04lx size=%dx%d\n",
997 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
998 lpitem->rect.bottom-lpitem->rect.top);
1002 if (lpitem->fType & MF_SEPARATOR)
1004 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1006 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1014 if (lpitem->hbmpItem) {
1017 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1018 /* Keep the size of the bitmap in callback mode to be able
1019 * to draw it correctly */
1020 lpitem->bmpsize = size;
1021 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1022 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1023 lpitem->rect.right += size.cx + 2;
1024 itemheight = size.cy + 2;
1026 if( !(lppop->dwStyle & MNS_NOCHECK))
1027 lpitem->rect.right += check_bitmap_width;
1028 lpitem->rect.right += 4 + menucharsize.cx;
1029 lpitem->xTab = lpitem->rect.right;
1030 lpitem->rect.right += arrow_bitmap_width;
1031 } else if (lpitem->hbmpItem) { /* menuBar */
1034 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1035 lpitem->bmpsize = size;
1036 lpitem->rect.right += size.cx;
1037 if( lpitem->text) lpitem->rect.right += 2;
1038 itemheight = size.cy;
1041 /* it must be a text item - unless it's the system menu */
1042 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1043 HFONT hfontOld = NULL;
1044 RECT rc = lpitem->rect;
1045 LONG txtheight, txtwidth;
1047 if ( lpitem->fState & MFS_DEFAULT ) {
1048 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1051 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1052 DT_SINGLELINE|DT_CALCRECT);
1053 lpitem->rect.right += rc.right - rc.left;
1054 itemheight = max( max( itemheight, txtheight),
1055 GetSystemMetrics( SM_CYMENU) - 1);
1056 lpitem->rect.right += 2 * menucharsize.cx;
1058 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1061 int n = (int)( p - lpitem->text);
1062 /* Item contains a tab (only meaningful in popup menus) */
1063 /* get text size before the tab */
1064 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 txtwidth = rc.right - rc.left;
1067 p += 1; /* advance past the Tab */
1068 /* get text size after the tab */
1069 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1070 DT_SINGLELINE|DT_CALCRECT);
1071 lpitem->xTab += txtwidth;
1072 txtheight = max( txtheight, tmpheight);
1073 txtwidth += menucharsize.cx + /* space for the tab */
1074 tmprc.right - tmprc.left; /* space for the short cut */
1076 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1077 DT_SINGLELINE|DT_CALCRECT);
1078 txtwidth = rc.right - rc.left;
1079 lpitem->xTab += txtwidth;
1081 lpitem->rect.right += 2 + txtwidth;
1082 itemheight = max( itemheight,
1083 max( txtheight + 2, menucharsize.cy + 4));
1085 if (hfontOld) SelectObject (hdc, hfontOld);
1086 } else if( menuBar) {
1087 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1089 lpitem->rect.bottom += itemheight;
1090 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1094 /***********************************************************************
1095 * MENU_GetMaxPopupHeight
1098 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1101 return lppop->cyMax;
1102 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1106 /***********************************************************************
1107 * MENU_PopupMenuCalcSize
1109 * Calculate the size of a popup menu.
1111 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1116 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1118 lppop->Width = lppop->Height = 0;
1119 if (lppop->nItems == 0) return;
1122 SelectObject( hdc, get_menu_font(FALSE));
1127 lppop->maxBmpSize.cx = 0;
1128 lppop->maxBmpSize.cy = 0;
1130 while (start < lppop->nItems)
1132 lpitem = &lppop->items[start];
1134 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1135 orgX += MENU_COL_SPACE;
1136 orgY = MENU_TOP_MARGIN;
1138 maxTab = maxTabWidth = 0;
1139 /* Parse items until column break or end of menu */
1140 for (i = start; i < lppop->nItems; i++, lpitem++)
1143 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1145 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1146 maxX = max( maxX, lpitem->rect.right );
1147 orgY = lpitem->rect.bottom;
1148 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1150 maxTab = max( maxTab, lpitem->xTab );
1151 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1155 /* Finish the column (set all items to the largest width found) */
1156 maxX = max( maxX, maxTab + maxTabWidth );
1157 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1159 lpitem->rect.right = maxX;
1160 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1161 lpitem->xTab = maxTab;
1164 lppop->Height = max( lppop->Height, orgY );
1167 lppop->Width = maxX;
1169 /* space for 3d border */
1170 lppop->Height += MENU_BOTTOM_MARGIN;
1173 /* Adjust popup height if it exceeds maximum */
1174 maxHeight = MENU_GetMaxPopupHeight(lppop);
1175 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1176 if (lppop->Height >= maxHeight)
1178 lppop->Height = maxHeight;
1179 lppop->bScrolling = TRUE;
1183 lppop->bScrolling = FALSE;
1186 ReleaseDC( 0, hdc );
1190 /***********************************************************************
1191 * MENU_MenuBarCalcSize
1193 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194 * height is off by 1 pixel which causes lengthy window relocations when
1195 * active document window is maximized/restored.
1197 * Calculate the size of the menu bar.
1199 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1200 LPPOPUPMENU lppop, HWND hwndOwner )
1203 int start, i, orgX, orgY, maxY, helpPos;
1205 if ((lprect == NULL) || (lppop == NULL)) return;
1206 if (lppop->nItems == 0) return;
1207 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1208 lppop->Width = lprect->right - lprect->left;
1210 maxY = lprect->top+1;
1213 lppop->maxBmpSize.cx = 0;
1214 lppop->maxBmpSize.cy = 0;
1215 while (start < lppop->nItems)
1217 lpitem = &lppop->items[start];
1218 orgX = lprect->left;
1221 /* Parse items until line break or end of menu */
1222 for (i = start; i < lppop->nItems; i++, lpitem++)
1224 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1226 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1228 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1229 debug_print_menuitem (" item: ", lpitem, "");
1230 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1232 if (lpitem->rect.right > lprect->right)
1234 if (i != start) break;
1235 else lpitem->rect.right = lprect->right;
1237 maxY = max( maxY, lpitem->rect.bottom );
1238 orgX = lpitem->rect.right;
1241 /* Finish the line (set all items to the largest height found) */
1242 while (start < i) lppop->items[start++].rect.bottom = maxY;
1245 lprect->bottom = maxY;
1246 lppop->Height = lprect->bottom - lprect->top;
1248 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1249 /* the last item (if several lines, only move the last line) */
1250 lpitem = &lppop->items[lppop->nItems-1];
1251 orgY = lpitem->rect.top;
1252 orgX = lprect->right;
1253 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1254 if ( (helpPos==-1) || (helpPos>i) )
1256 if (lpitem->rect.top != orgY) break; /* Other line */
1257 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1258 lpitem->rect.left += orgX - lpitem->rect.right;
1259 lpitem->rect.right = orgX;
1260 orgX = lpitem->rect.left;
1265 /***********************************************************************
1266 * MENU_DrawScrollArrows
1268 * Draw scroll arrows.
1271 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1273 HDC hdcMem = CreateCompatibleDC(hdc);
1274 HBITMAP hOrigBitmap;
1275 UINT arrow_bitmap_width, arrow_bitmap_height;
1279 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1280 arrow_bitmap_width = bmp.bmWidth;
1281 arrow_bitmap_height = bmp.bmHeight;
1284 if (lppop->nScrollPos)
1285 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1287 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1290 rect.right = lppop->Width;
1291 rect.bottom = arrow_bitmap_height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1294 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1295 rect.top = lppop->Height - arrow_bitmap_height;
1296 rect.bottom = lppop->Height;
1297 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1298 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1299 SelectObject(hdcMem, get_down_arrow_bitmap());
1301 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1302 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1303 lppop->Height - arrow_bitmap_height,
1304 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1305 SelectObject(hdcMem, hOrigBitmap);
1310 /***********************************************************************
1313 * Draws the popup-menu arrow.
1315 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1316 UINT arrow_bitmap_height)
1318 HDC hdcMem = CreateCompatibleDC( hdc );
1319 HBITMAP hOrigBitmap;
1321 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1322 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1323 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1324 arrow_bitmap_width, arrow_bitmap_height,
1325 hdcMem, 0, 0, SRCCOPY );
1326 SelectObject( hdcMem, hOrigBitmap );
1329 /***********************************************************************
1332 * Draw a single menu item.
1334 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1335 UINT height, BOOL menuBar, UINT odaction )
1338 BOOL flat_menu = FALSE;
1340 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1341 POPUPMENU *menu = MENU_GetMenu(hmenu);
1344 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1348 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1349 arrow_bitmap_width = bmp.bmWidth;
1350 arrow_bitmap_height = bmp.bmHeight;
1353 if (lpitem->fType & MF_SYSMENU)
1355 if( !IsIconic(hwnd) )
1356 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1360 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1361 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1365 if (lpitem->fState & MF_HILITE)
1367 if(menuBar && !flat_menu) {
1368 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1369 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1371 if(lpitem->fState & MF_GRAYED)
1372 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1374 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1375 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1380 if (lpitem->fState & MF_GRAYED)
1381 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1383 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1384 SetBkColor( hdc, GetSysColor( bkgnd ) );
1387 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1388 rect = lpitem->rect;
1389 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1391 if (lpitem->fType & MF_OWNERDRAW)
1394 ** Experimentation under Windows reveals that an owner-drawn
1395 ** menu is given the rectangle which includes the space it requested
1396 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397 ** and a popup-menu arrow. This is the value of lpitem->rect.
1398 ** Windows will leave all drawing to the application except for
1399 ** the popup-menu arrow. Windows always draws that itself, after
1400 ** the menu owner has finished drawing.
1404 dis.CtlType = ODT_MENU;
1406 dis.itemID = lpitem->wID;
1407 dis.itemData = lpitem->dwItemData;
1409 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1410 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1411 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1412 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413 dis.hwndItem = (HWND)hmenu;
1416 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1418 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1419 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1420 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1421 /* Draw the popup-menu arrow */
1422 if (lpitem->fType & MF_POPUP)
1423 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1424 arrow_bitmap_height);
1428 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1430 if (lpitem->fState & MF_HILITE)
1434 InflateRect (&rect, -1, -1);
1435 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1436 InflateRect (&rect, 1, 1);
1437 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1442 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1444 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1448 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1450 SetBkMode( hdc, TRANSPARENT );
1452 /* vertical separator */
1453 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1458 rc.left -= MENU_COL_SPACE / 2 + 1;
1460 rc.bottom = height - 3;
1463 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1464 MoveToEx( hdc, rc.left, rc.top, NULL );
1465 LineTo( hdc, rc.left, rc.bottom );
1466 SelectObject( hdc, oldPen );
1469 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1472 /* horizontal separator */
1473 if (lpitem->fType & MF_SEPARATOR)
1480 rc.top = ( rc.top + rc.bottom) / 2;
1483 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1484 MoveToEx( hdc, rc.left, rc.top, NULL );
1485 LineTo( hdc, rc.right, rc.top );
1486 SelectObject( hdc, oldPen );
1489 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1493 /* helper lines for debugging */
1494 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1495 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1496 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1497 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1500 if (lpitem->hbmpItem) {
1501 /* calculate the bitmap rectangle in coordinates relative
1502 * to the item rectangle */
1504 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1507 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1510 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1511 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1513 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1514 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1517 bmprc.top = (rect.bottom - rect.top -
1518 lpitem->bmpsize.cy) / 2;
1519 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1525 INT y = rect.top + rect.bottom;
1527 int checked = FALSE;
1528 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1529 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1530 /* Draw the check mark
1533 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1535 if( !(menu->dwStyle & MNS_NOCHECK)) {
1536 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1537 lpitem->hUnCheckBit;
1538 if (bm) /* we have a custom bitmap */
1540 HDC hdcMem = CreateCompatibleDC( hdc );
1542 SelectObject( hdcMem, bm );
1543 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1544 check_bitmap_width, check_bitmap_height,
1545 hdcMem, 0, 0, SRCCOPY );
1549 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1552 HBITMAP bm = CreateBitmap( check_bitmap_width,
1553 check_bitmap_height, 1, 1, NULL );
1554 HDC hdcMem = CreateCompatibleDC( hdc );
1556 SelectObject( hdcMem, bm );
1557 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1558 DrawFrameControl( hdcMem, &r, DFC_MENU,
1559 (lpitem->fType & MFT_RADIOCHECK) ?
1560 DFCS_MENUBULLET : DFCS_MENUCHECK );
1561 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1562 hdcMem, 0, 0, SRCCOPY );
1568 if( lpitem->hbmpItem &&
1569 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1571 /* some applications make this assumption on the DC's origin */
1572 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1573 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1575 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1577 /* Draw the popup-menu arrow */
1578 if (lpitem->fType & MF_POPUP)
1579 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1580 arrow_bitmap_height);
1582 if( !(menu->dwStyle & MNS_NOCHECK))
1583 rect.left += check_bitmap_width;
1584 rect.right -= arrow_bitmap_width;
1586 else if( lpitem->hbmpItem)
1587 { /* Draw the bitmap */
1590 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1591 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1593 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1595 /* process text if present */
1601 UINT uFormat = (menuBar) ?
1602 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1603 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1605 if( !(menu->dwStyle & MNS_CHECKORBMP))
1606 rect.left += menu->maxBmpSize.cx;
1608 if ( lpitem->fState & MFS_DEFAULT )
1610 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1614 if( lpitem->hbmpItem)
1615 rect.left += lpitem->bmpsize.cx;
1616 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1617 rect.left += menucharsize.cx;
1618 rect.right -= menucharsize.cx;
1621 for (i = 0; lpitem->text[i]; i++)
1622 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1625 if(lpitem->fState & MF_GRAYED)
1627 if (!(lpitem->fState & MF_HILITE) )
1629 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1630 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1631 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1632 --rect.left; --rect.top; --rect.right; --rect.bottom;
1634 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1637 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1639 /* paint the shortcut text */
1640 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1642 if (lpitem->text[i] == '\t')
1644 rect.left = lpitem->xTab;
1645 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1649 rect.right = lpitem->xTab;
1650 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1653 if(lpitem->fState & MF_GRAYED)
1655 if (!(lpitem->fState & MF_HILITE) )
1657 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1658 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1659 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1660 --rect.left; --rect.top; --rect.right; --rect.bottom;
1662 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1664 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1668 SelectObject (hdc, hfontOld);
1673 /***********************************************************************
1674 * MENU_DrawPopupMenu
1676 * Paint a popup menu.
1678 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1680 HBRUSH hPrevBrush = 0;
1683 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1685 GetClientRect( hwnd, &rect );
1687 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1688 && (SelectObject( hdc, get_menu_font(FALSE))))
1692 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1694 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1698 BOOL flat_menu = FALSE;
1700 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1702 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1704 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1706 if( (menu = MENU_GetMenu( hmenu )))
1708 /* draw menu items */
1715 for( u = menu->nItems; u > 0; u--, item++)
1716 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1717 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1719 /* draw scroll arrows */
1720 if (menu->bScrolling)
1721 MENU_DrawScrollArrows(menu, hdc);
1725 SelectObject( hdc, hPrevBrush );
1730 /***********************************************************************
1733 * Paint a menu bar. Returns the height of the menu bar.
1734 * called from [windows/nonclient.c]
1736 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1741 HMENU hMenu = GetMenu(hwnd);
1743 lppop = MENU_GetMenu( hMenu );
1744 if (lppop == NULL || lprect == NULL)
1746 return GetSystemMetrics(SM_CYMENU);
1751 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1753 if (lppop->Height == 0)
1754 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1756 lprect->bottom = lprect->top + lppop->Height;
1758 if (hfontOld) SelectObject( hDC, hfontOld);
1759 return lppop->Height;
1762 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1766 /***********************************************************************
1769 * Display a popup menu.
1771 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1772 INT x, INT y, INT xanchor, INT yanchor )
1780 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1781 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1783 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1784 if (menu->FocusedItem != NO_SELECTED_ITEM)
1786 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1787 menu->FocusedItem = NO_SELECTED_ITEM;
1790 /* store the owner for DrawItem */
1791 menu->hwndOwner = hwndOwner;
1793 menu->nScrollPos = 0;
1794 MENU_PopupMenuCalcSize( menu );
1796 /* adjust popup menu pos so that it fits within the desktop */
1798 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1799 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1801 /* FIXME: should use item rect */
1804 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1805 info.cbSize = sizeof(info);
1806 GetMonitorInfoW( monitor, &info );
1807 if( x + width > info.rcWork.right)
1809 if( xanchor && x >= width - xanchor )
1810 x -= width - xanchor;
1812 if( x + width > info.rcWork.right)
1813 x = info.rcWork.right - width;
1815 if( x < info.rcWork.left ) x = info.rcWork.left;
1817 if( y + height > info.rcWork.bottom)
1819 if( yanchor && y >= height + yanchor )
1820 y -= height + yanchor;
1822 if( y + height > info.rcWork.bottom)
1823 y = info.rcWork.bottom - height;
1825 if( y < info.rcWork.top ) y = info.rcWork.top;
1827 /* NOTE: In Windows, top menu popup is not owned. */
1828 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1829 WS_POPUP, x, y, width, height,
1830 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1832 if( !menu->hWnd ) return FALSE;
1833 if (!top_popup) top_popup = menu->hWnd;
1835 /* Display the window */
1837 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1838 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1839 UpdateWindow( menu->hWnd );
1844 /***********************************************************************
1845 * MENU_EnsureMenuItemVisible
1848 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1850 if (lppop->bScrolling)
1852 MENUITEM *item = &lppop->items[wIndex];
1853 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1854 UINT nOldPos = lppop->nScrollPos;
1856 UINT arrow_bitmap_height;
1859 GetClientRect(lppop->hWnd, &rc);
1861 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1862 arrow_bitmap_height = bmp.bmHeight;
1864 rc.top += arrow_bitmap_height;
1865 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1867 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1868 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1871 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1872 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1873 MENU_DrawScrollArrows(lppop, hdc);
1875 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1877 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1878 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1879 MENU_DrawScrollArrows(lppop, hdc);
1885 /***********************************************************************
1888 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1889 BOOL sendMenuSelect, HMENU topmenu )
1894 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1896 lppop = MENU_GetMenu( hmenu );
1897 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1899 if (lppop->FocusedItem == wIndex) return;
1900 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1901 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1902 if (!top_popup) top_popup = lppop->hWnd;
1904 SelectObject( hdc, get_menu_font(FALSE));
1906 /* Clear previous highlighted item */
1907 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1909 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1910 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1911 lppop->Height, !(lppop->wFlags & MF_POPUP),
1915 /* Highlight new item (if any) */
1916 lppop->FocusedItem = wIndex;
1917 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1919 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1920 lppop->items[wIndex].fState |= MF_HILITE;
1921 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1922 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1923 &lppop->items[wIndex], lppop->Height,
1924 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1928 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1929 SendMessageW( hwndOwner, WM_MENUSELECT,
1930 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1931 ip->fType | ip->fState |
1932 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1935 else if (sendMenuSelect) {
1938 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1939 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1940 MENUITEM *ip = &ptm->items[pos];
1941 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1942 ip->fType | ip->fState |
1943 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1947 ReleaseDC( lppop->hWnd, hdc );
1951 /***********************************************************************
1952 * MENU_MoveSelection
1954 * Moves currently selected item according to the offset parameter.
1955 * If there is no selection then it should select the last item if
1956 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1958 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1963 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1965 menu = MENU_GetMenu( hmenu );
1966 if ((!menu) || (!menu->items)) return;
1968 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1970 if( menu->nItems == 1 ) return; else
1971 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1973 if (!(menu->items[i].fType & MF_SEPARATOR))
1975 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1980 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1981 i >= 0 && i < menu->nItems ; i += offset)
1982 if (!(menu->items[i].fType & MF_SEPARATOR))
1984 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1990 /**********************************************************************
1993 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1996 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1999 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2000 TRACE("flags=%x str=%p\n", flags, str);
2002 if (IS_STRING_ITEM(flags))
2004 LPWSTR prevText = item->text;
2007 flags |= MF_SEPARATOR;
2013 /* Item beginning with a backspace is a help item */
2019 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2021 strcpyW( text, str );
2024 item->hbmpItem = NULL;
2025 HeapFree( GetProcessHeap(), 0, prevText );
2027 else if(( flags & MFT_BITMAP)) {
2028 item->hbmpItem = HBITMAP_32(LOWORD(str));
2029 /* setting bitmap clears text */
2030 HeapFree( GetProcessHeap(), 0, item->text );
2034 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2036 if (flags & MF_OWNERDRAW)
2037 item->dwItemData = (DWORD_PTR)str;
2039 item->dwItemData = 0;
2041 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2042 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2044 if (flags & MF_POPUP)
2046 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2047 if (menu) menu->wFlags |= MF_POPUP;
2059 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2061 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2062 flags |= MF_POPUP; /* keep popup */
2064 item->fType = flags & TYPE_MASK;
2065 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2066 for ModifyMenu, but Windows accepts it */
2067 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2069 /* Don't call SetRectEmpty here! */
2071 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2076 /**********************************************************************
2079 * Insert (allocate) a new item into a menu.
2081 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2086 if (!(menu = MENU_GetMenu(hMenu)))
2089 /* Find where to insert new item */
2091 if (flags & MF_BYPOSITION) {
2092 if (pos > menu->nItems)
2095 if (!MENU_FindItem( &hMenu, &pos, flags ))
2098 if (!(menu = MENU_GetMenu( hMenu )))
2103 /* Make sure that MDI system buttons stay on the right side.
2104 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2105 * regardless of their id.
2107 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2108 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2109 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2112 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2114 /* Create new items array */
2116 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2119 WARN("allocation failed\n" );
2122 if (menu->nItems > 0)
2124 /* Copy the old array into the new one */
2125 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2126 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2127 (menu->nItems-pos)*sizeof(MENUITEM) );
2128 HeapFree( GetProcessHeap(), 0, menu->items );
2130 menu->items = newItems;
2132 memset( &newItems[pos], 0, sizeof(*newItems) );
2133 menu->Height = 0; /* force size recalculate */
2134 return &newItems[pos];
2138 /**********************************************************************
2139 * MENU_ParseResource
2141 * Parse a standard menu resource and add items to the menu.
2142 * Return a pointer to the end of the resource.
2144 * NOTE: flags is equivalent to the mtOption field
2146 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2154 flags = GET_WORD(res);
2155 end_flag = flags & MF_END;
2156 /* Remove MF_END because it has the same value as MF_HILITE */
2158 res += sizeof(WORD);
2159 if (!(flags & MF_POPUP))
2162 res += sizeof(WORD);
2165 if (!unicode) res += strlen(str) + 1;
2166 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2167 if (flags & MF_POPUP)
2169 HMENU hSubMenu = CreatePopupMenu();
2170 if (!hSubMenu) return NULL;
2171 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2173 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2174 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2176 else /* Not a popup */
2178 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2179 else AppendMenuW( hMenu, flags, id,
2180 *(LPCWSTR)str ? (LPCWSTR)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 )
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 submenu = MENU_GetMenu( hsubmenu );
2293 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2294 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2295 DestroyWindow( submenu->hWnd );
2301 /***********************************************************************
2304 * Display the sub-menu of the selected item of this menu.
2305 * Return the handle of the submenu, or hmenu if no submenu to display.
2307 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2308 BOOL selectFirst, UINT wFlags )
2315 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2317 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2319 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2321 item = &menu->items[menu->FocusedItem];
2322 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2325 /* message must be sent before using item,
2326 because nearly everything may be changed by the application ! */
2328 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2329 if (!(wFlags & TPM_NONOTIFY))
2330 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2331 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2333 item = &menu->items[menu->FocusedItem];
2336 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2337 if (!(item->fState & MF_HILITE))
2339 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2340 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2342 SelectObject( hdc, get_menu_font(FALSE));
2344 item->fState |= MF_HILITE;
2345 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2346 ReleaseDC( menu->hWnd, hdc );
2348 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2351 item->fState |= MF_MOUSESELECT;
2353 if (IS_SYSTEM_MENU(menu))
2355 MENU_InitSysMenuPopup(item->hSubMenu,
2356 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2357 GetClassLongW( menu->hWnd, GCL_STYLE));
2359 NC_GetSysPopupPos( menu->hWnd, &rect );
2360 rect.top = rect.bottom;
2361 rect.right = GetSystemMetrics(SM_CXSIZE);
2362 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2366 GetWindowRect( menu->hWnd, &rect );
2367 if (menu->wFlags & MF_POPUP)
2369 RECT rc = item->rect;
2371 MENU_AdjustMenuItemRect(menu, &rc);
2373 /* The first item in the popup menu has to be at the
2374 same y position as the focused menu item */
2375 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2376 rect.top += rc.top - MENU_TOP_MARGIN;
2377 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2378 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2379 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2383 rect.left += item->rect.left;
2384 rect.top += item->rect.bottom;
2385 rect.right = item->rect.right - item->rect.left;
2386 rect.bottom = item->rect.bottom - item->rect.top;
2390 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2391 rect.left, rect.top, rect.right, rect.bottom );
2393 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2394 return item->hSubMenu;
2399 /**********************************************************************
2402 HWND MENU_IsMenuActive(void)
2407 /***********************************************************************
2410 * Walks menu chain trying to find a menu pt maps to.
2412 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2414 POPUPMENU *menu = MENU_GetMenu( hMenu );
2415 UINT item = menu->FocusedItem;
2418 /* try subpopup first (if any) */
2419 ret = (item != NO_SELECTED_ITEM &&
2420 (menu->items[item].fType & MF_POPUP) &&
2421 (menu->items[item].fState & MF_MOUSESELECT))
2422 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2424 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2426 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2427 if( menu->wFlags & MF_POPUP )
2429 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2431 else if (ht == HTSYSMENU)
2432 ret = get_win_sys_menu( menu->hWnd );
2433 else if (ht == HTMENU)
2434 ret = GetMenu( menu->hWnd );
2439 /***********************************************************************
2440 * MENU_ExecFocusedItem
2442 * Execute a menu item (for instance when user pressed Enter).
2443 * Return the wID of the executed item. Otherwise, -1 indicating
2444 * that no menu item was executed, -2 if a popup is shown;
2445 * Have to receive the flags for the TrackPopupMenu options to avoid
2446 * sending unwanted message.
2449 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2452 POPUPMENU *menu = MENU_GetMenu( hMenu );
2454 TRACE("%p hmenu=%p\n", pmt, hMenu);
2456 if (!menu || !menu->nItems ||
2457 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2459 item = &menu->items[menu->FocusedItem];
2461 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2463 if (!(item->fType & MF_POPUP))
2465 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2467 /* If TPM_RETURNCMD is set you return the id, but
2468 do not send a message to the owner */
2469 if(!(wFlags & TPM_RETURNCMD))
2471 if( menu->wFlags & MF_SYSMENU )
2472 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2473 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2476 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2477 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2480 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2488 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2495 /***********************************************************************
2496 * MENU_SwitchTracking
2498 * Helper function for menu navigation routines.
2500 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2502 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2503 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2505 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2507 if( pmt->hTopMenu != hPtMenu &&
2508 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2510 /* both are top level menus (system and menu-bar) */
2511 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2512 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2513 pmt->hTopMenu = hPtMenu;
2515 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2516 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2520 /***********************************************************************
2523 * Return TRUE if we can go on with menu tracking.
2525 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2527 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2532 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2535 if( IS_SYSTEM_MENU(ptmenu) )
2536 item = ptmenu->items;
2538 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2542 if( ptmenu->FocusedItem != id )
2543 MENU_SwitchTracking( pmt, hPtMenu, id );
2545 /* If the popup menu is not already "popped" */
2546 if(!(item->fState & MF_MOUSESELECT ))
2548 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2553 /* Else the click was on the menu bar, finish the tracking */
2558 /***********************************************************************
2561 * Return the value of MENU_ExecFocusedItem if
2562 * the selected item was not a popup. Else open the popup.
2563 * A -1 return value indicates that we go on with menu tracking.
2566 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2568 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2573 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2576 if( IS_SYSTEM_MENU(ptmenu) )
2577 item = ptmenu->items;
2579 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2581 if( item && (ptmenu->FocusedItem == id ))
2583 debug_print_menuitem ("FocusedItem: ", item, "");
2585 if( !(item->fType & MF_POPUP) )
2587 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2588 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2589 return executedMenuId;
2592 /* If we are dealing with the top-level menu */
2593 /* and this is a click on an already "popped" item: */
2594 /* Stop the menu tracking and close the opened submenus */
2595 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2598 ptmenu->bTimeToHide = TRUE;
2604 /***********************************************************************
2607 * Return TRUE if we can go on with menu tracking.
2609 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2611 UINT id = NO_SELECTED_ITEM;
2612 POPUPMENU *ptmenu = NULL;
2616 ptmenu = MENU_GetMenu( hPtMenu );
2617 if( IS_SYSTEM_MENU(ptmenu) )
2620 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2623 if( id == NO_SELECTED_ITEM )
2625 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2626 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2629 else if( ptmenu->FocusedItem != id )
2631 MENU_SwitchTracking( pmt, hPtMenu, id );
2632 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2638 /***********************************************************************
2641 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2643 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2645 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2648 /* When skipping left, we need to do something special after the
2650 if (vk == VK_LEFT && menu->FocusedItem == 0)
2654 /* When skipping right, for the non-system menu, we need to
2655 handle the last non-special menu item (ie skip any window
2656 icons such as MDI maximize, restore or close) */
2657 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2659 int i = menu->FocusedItem + 1;
2660 while (i < menu->nItems) {
2661 if ((menu->items[i].wID >= SC_SIZE &&
2662 menu->items[i].wID <= SC_RESTORE)) {
2666 if (i == menu->nItems) {
2670 /* When skipping right, we need to cater for the system menu */
2671 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2673 if (menu->FocusedItem == (menu->nItems - 1)) {
2680 MDINEXTMENU next_menu;
2685 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2686 next_menu.hmenuNext = 0;
2687 next_menu.hwndNext = 0;
2688 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2690 TRACE("%p [%p] -> %p [%p]\n",
2691 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2693 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2695 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2696 hNewWnd = pmt->hOwnerWnd;
2697 if( IS_SYSTEM_MENU(menu) )
2699 /* switch to the menu bar */
2701 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2705 menu = MENU_GetMenu( hNewMenu );
2706 id = menu->nItems - 1;
2708 /* Skip backwards over any system predefined icons,
2709 eg. MDI close, restore etc icons */
2711 (menu->items[id].wID >= SC_SIZE &&
2712 menu->items[id].wID <= SC_RESTORE)) id--;
2715 else if (style & WS_SYSMENU )
2717 /* switch to the system menu */
2718 hNewMenu = get_win_sys_menu( hNewWnd );
2722 else /* application returned a new menu to switch to */
2724 hNewMenu = next_menu.hmenuNext;
2725 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2727 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2729 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2731 if (style & WS_SYSMENU &&
2732 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2734 /* get the real system menu */
2735 hNewMenu = get_win_sys_menu(hNewWnd);
2737 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2739 /* FIXME: Not sure what to do here;
2740 * perhaps try to track hNewMenu as a popup? */
2742 TRACE(" -- got confused.\n");
2749 if( hNewMenu != pmt->hTopMenu )
2751 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2753 if( pmt->hCurrentMenu != pmt->hTopMenu )
2754 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2757 if( hNewWnd != pmt->hOwnerWnd )
2759 pmt->hOwnerWnd = hNewWnd;
2760 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2763 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2764 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2771 /***********************************************************************
2774 * The idea is not to show the popup if the next input message is
2775 * going to hide it anyway.
2777 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2781 msg.hwnd = pmt->hOwnerWnd;
2783 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2784 pmt->trackFlags |= TF_SKIPREMOVE;
2789 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2790 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2792 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2793 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2794 if( msg.message == WM_KEYDOWN &&
2795 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2797 pmt->trackFlags |= TF_SUSPENDPOPUP;
2804 /* failures go through this */
2805 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2809 /***********************************************************************
2812 * Handle a VK_ESCAPE key event in a menu.
2814 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2816 BOOL bEndMenu = TRUE;
2818 if (pmt->hCurrentMenu != pmt->hTopMenu)
2820 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2822 if (menu->wFlags & MF_POPUP)
2824 HMENU hmenutmp, hmenuprev;
2826 hmenuprev = hmenutmp = pmt->hTopMenu;
2828 /* close topmost popup */
2829 while (hmenutmp != pmt->hCurrentMenu)
2831 hmenuprev = hmenutmp;
2832 hmenutmp = MENU_GetSubPopup( hmenuprev );
2835 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2836 pmt->hCurrentMenu = hmenuprev;
2844 /***********************************************************************
2847 * Handle a VK_LEFT key event in a menu.
2849 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2852 HMENU hmenutmp, hmenuprev;
2855 hmenuprev = hmenutmp = pmt->hTopMenu;
2856 menu = MENU_GetMenu( hmenutmp );
2858 /* Try to move 1 column left (if possible) */
2859 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2860 NO_SELECTED_ITEM ) {
2862 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2867 /* close topmost popup */
2868 while (hmenutmp != pmt->hCurrentMenu)
2870 hmenuprev = hmenutmp;
2871 hmenutmp = MENU_GetSubPopup( hmenuprev );
2874 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2875 pmt->hCurrentMenu = hmenuprev;
2877 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2879 /* move menu bar selection if no more popups are left */
2881 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2882 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2884 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2886 /* A sublevel menu was displayed - display the next one
2887 * unless there is another displacement coming up */
2889 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2890 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2891 pmt->hTopMenu, TRUE, wFlags);
2897 /***********************************************************************
2900 * Handle a VK_RIGHT key event in a menu.
2902 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2905 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2908 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2910 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2911 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2913 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2915 /* If already displaying a popup, try to display sub-popup */
2917 hmenutmp = pmt->hCurrentMenu;
2918 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2920 /* if subpopup was displayed then we are done */
2921 if (hmenutmp != pmt->hCurrentMenu) return;
2924 /* Check to see if there's another column */
2925 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2926 NO_SELECTED_ITEM ) {
2927 TRACE("Going to %d.\n", nextcol );
2928 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2933 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2935 if( pmt->hCurrentMenu != pmt->hTopMenu )
2937 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2938 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2939 } else hmenutmp = 0;
2941 /* try to move to the next item */
2942 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2943 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2945 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2946 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2947 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2948 pmt->hTopMenu, TRUE, wFlags);
2952 /***********************************************************************
2955 * Menu tracking code.
2957 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2958 HWND hwnd, const RECT *lprect )
2963 INT executedMenuId = -1;
2965 BOOL enterIdleSent = FALSE;
2968 mt.hCurrentMenu = hmenu;
2969 mt.hTopMenu = hmenu;
2970 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2974 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2975 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2978 if (!(menu = MENU_GetMenu( hmenu )))
2980 WARN("Invalid menu handle %p\n", hmenu);
2981 SetLastError(ERROR_INVALID_MENU_HANDLE);
2985 if (wFlags & TPM_BUTTONDOWN)
2987 /* Get the result in order to start the tracking or not */
2988 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2989 fEndMenu = !fRemove;
2992 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2994 set_capture_window( mt.hOwnerWnd, GUI_INMENUMODE, NULL );
2998 menu = MENU_GetMenu( mt.hCurrentMenu );
2999 if (!menu) /* sometimes happens if I do a window manager close */
3002 /* we have to keep the message in the queue until it's
3003 * clear that menu loop is not over yet. */
3007 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3009 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3010 /* remove the message from the queue */
3011 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3017 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3018 enterIdleSent = TRUE;
3019 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3025 /* check if EndMenu() tried to cancel us, by posting this message */
3026 if(msg.message == WM_CANCELMODE)
3028 /* we are now out of the loop */
3031 /* remove the message from the queue */
3032 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3034 /* break out of internal loop, ala ESCAPE */
3038 TranslateMessage( &msg );
3041 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3042 enterIdleSent=FALSE;
3045 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3048 * Use the mouse coordinates in lParam instead of those in the MSG
3049 * struct to properly handle synthetic messages. They are already
3050 * in screen coordinates.
3052 mt.pt.x = (short)LOWORD(msg.lParam);
3053 mt.pt.y = (short)HIWORD(msg.lParam);
3055 /* Find a menu for this mouse event */
3056 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3060 /* no WM_NC... messages in captured state */
3062 case WM_RBUTTONDBLCLK:
3063 case WM_RBUTTONDOWN:
3064 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3066 case WM_LBUTTONDBLCLK:
3067 case WM_LBUTTONDOWN:
3068 /* If the message belongs to the menu, removes it from the queue */
3069 /* Else, end menu tracking */
3070 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3071 fEndMenu = !fRemove;
3075 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3078 /* Check if a menu was selected by the mouse */
3081 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3082 TRACE("executedMenuId %d\n", executedMenuId);
3084 /* End the loop if executedMenuId is an item ID */
3085 /* or if the job was done (executedMenuId = 0). */
3086 fEndMenu = fRemove = (executedMenuId != -1);
3088 /* No menu was selected by the mouse */
3089 /* if the function was called by TrackPopupMenu, continue
3090 with the menu tracking. If not, stop it */
3092 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3097 /* the selected menu item must be changed every time */
3098 /* the mouse moves. */
3101 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3103 } /* switch(msg.message) - mouse */
3105 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3107 fRemove = TRUE; /* Keyboard messages are always removed */
3120 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3121 NO_SELECTED_ITEM, FALSE, 0 );
3122 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3123 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3127 case VK_DOWN: /* If on menu bar, pull-down the menu */
3129 menu = MENU_GetMenu( mt.hCurrentMenu );
3130 if (!(menu->wFlags & MF_POPUP))
3131 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3132 else /* otherwise try to move selection */
3133 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3134 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3138 MENU_KeyLeft( &mt, wFlags );
3142 MENU_KeyRight( &mt, wFlags );
3146 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3152 hi.cbSize = sizeof(HELPINFO);
3153 hi.iContextType = HELPINFO_MENUITEM;
3154 if (menu->FocusedItem == NO_SELECTED_ITEM)
3157 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3158 hi.hItemHandle = hmenu;
3159 hi.dwContextId = menu->dwContextHelpID;
3160 hi.MousePos = msg.pt;
3161 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3168 break; /* WM_KEYDOWN */
3175 if (msg.wParam == '\r' || msg.wParam == ' ')
3177 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3178 fEndMenu = (executedMenuId != -2);
3183 /* Hack to avoid control chars. */
3184 /* We will find a better way real soon... */
3185 if (msg.wParam < 32) break;
3187 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3188 LOWORD(msg.wParam), FALSE );
3189 if (pos == (UINT)-2) fEndMenu = TRUE;
3190 else if (pos == (UINT)-1) MessageBeep(0);
3193 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3195 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3196 fEndMenu = (executedMenuId != -2);
3200 } /* switch(msg.message) - kbd */
3204 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3205 DispatchMessageW( &msg );
3209 if (!fEndMenu) fRemove = TRUE;
3211 /* finally remove message from the queue */
3213 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3214 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3215 else mt.trackFlags &= ~TF_SKIPREMOVE;
3218 set_capture_window( 0, GUI_INMENUMODE, NULL );
3220 /* If dropdown is still painted and the close box is clicked on
3221 then the menu will be destroyed as part of the DispatchMessage above.
3222 This will then invalidate the menu handle in mt.hTopMenu. We should
3223 check for this first. */
3224 if( IsMenu( mt.hTopMenu ) )
3226 menu = MENU_GetMenu( mt.hTopMenu );
3228 if( IsWindow( mt.hOwnerWnd ) )
3230 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3232 if (menu && (menu->wFlags & MF_POPUP))
3234 DestroyWindow( menu->hWnd );
3237 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3238 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3241 /* Reset the variable for hiding menu */
3242 if( menu ) menu->bTimeToHide = FALSE;
3245 /* The return value is only used by TrackPopupMenu */
3246 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3247 if (executedMenuId == -1) executedMenuId = 0;
3248 return executedMenuId;
3251 /***********************************************************************
3254 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3258 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3262 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3263 if (!(wFlags & TPM_NONOTIFY))
3264 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3266 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3268 if (!(wFlags & TPM_NONOTIFY))
3270 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3271 /* If an app changed/recreated menu bar entries in WM_INITMENU
3272 * menu sizes will be recalculated once the menu created/shown.
3276 /* This makes the menus of applications built with Delphi work.
3277 * It also enables menus to be displayed in more than one window,
3278 * but there are some bugs left that need to be fixed in this case.
3280 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3284 /***********************************************************************
3287 static BOOL MENU_ExitTracking(HWND hWnd)
3289 TRACE("hwnd=%p\n", hWnd);
3291 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3297 /***********************************************************************
3298 * MENU_TrackMouseMenuBar
3300 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3302 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3304 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3305 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3307 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3311 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3312 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3313 MENU_ExitTracking(hWnd);
3318 /***********************************************************************
3319 * MENU_TrackKbdMenuBar
3321 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3323 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3325 UINT uItem = NO_SELECTED_ITEM;
3327 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3329 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3331 /* find window that has a menu */
3333 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3334 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3336 /* check if we have to track a system menu */
3338 hTrackMenu = GetMenu( hwnd );
3339 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3341 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3342 hTrackMenu = get_win_sys_menu( hwnd );
3344 wParam |= HTSYSMENU; /* prevent item lookup */
3347 if (!IsMenu( hTrackMenu )) return;
3349 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3351 if( wChar && wChar != ' ' )
3353 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3354 if ( uItem >= (UINT)(-2) )
3356 if( uItem == (UINT)(-1) ) MessageBeep(0);
3357 /* schedule end of menu tracking */
3358 wFlags |= TF_ENDMENU;
3363 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3365 if (wParam & HTSYSMENU && wChar != ' ')
3367 /* prevent sysmenu activation for managed windows on Alt down/up */
3368 if (GetPropA( hwnd, "__wine_x11_managed" ))
3369 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3373 if( uItem == NO_SELECTED_ITEM )
3374 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3376 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3380 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3381 MENU_ExitTracking( hwnd );
3385 /**********************************************************************
3386 * TrackPopupMenu (USER32.@)
3388 * Like the win32 API, the function return the command ID only if the
3389 * flag TPM_RETURNCMD is on.
3392 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3393 INT nReserved, HWND hWnd, const RECT *lpRect )
3397 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3398 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3400 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3402 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3403 if (!(wFlags & TPM_NONOTIFY))
3404 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3406 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3407 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3408 MENU_ExitTracking(hWnd);
3413 /**********************************************************************
3414 * TrackPopupMenuEx (USER32.@)
3416 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3417 HWND hWnd, LPTPMPARAMS lpTpm )
3419 FIXME("not fully implemented\n" );
3420 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3421 lpTpm ? &lpTpm->rcExclude : NULL );
3424 /***********************************************************************
3427 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3429 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3431 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3437 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3438 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3442 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3443 return MA_NOACTIVATE;
3448 BeginPaint( hwnd, &ps );
3449 MENU_DrawPopupMenu( hwnd, ps.hdc,
3450 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3451 EndPaint( hwnd, &ps );
3458 /* zero out global pointer in case resident popup window was destroyed. */
3459 if (hwnd == top_popup) top_popup = 0;
3466 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3469 SetWindowLongPtrW( hwnd, 0, 0 );
3472 case MM_SETMENUHANDLE:
3473 SetWindowLongPtrW( hwnd, 0, wParam );
3476 case MM_GETMENUHANDLE:
3477 return GetWindowLongPtrW( hwnd, 0 );
3480 return DefWindowProcW( hwnd, message, wParam, lParam );
3486 /***********************************************************************
3487 * MENU_GetMenuBarHeight
3489 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3491 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3492 INT orgX, INT orgY )
3498 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3500 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3502 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3503 SelectObject( hdc, get_menu_font(FALSE));
3504 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3505 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3506 ReleaseDC( hwnd, hdc );
3507 return lppop->Height;
3511 /*******************************************************************
3512 * ChangeMenuA (USER32.@)
3514 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3515 UINT id, UINT flags )
3517 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3518 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3520 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3521 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3523 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3524 flags & MF_BYPOSITION ? pos : id,
3525 flags & ~MF_REMOVE );
3526 /* Default: MF_INSERT */
3527 return InsertMenuA( hMenu, pos, flags, id, data );
3531 /*******************************************************************
3532 * ChangeMenuW (USER32.@)
3534 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3535 UINT id, UINT flags )
3537 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3538 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3540 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3541 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3543 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3544 flags & MF_BYPOSITION ? pos : id,
3545 flags & ~MF_REMOVE );
3546 /* Default: MF_INSERT */
3547 return InsertMenuW( hMenu, pos, flags, id, data );
3551 /*******************************************************************
3552 * CheckMenuItem (USER32.@)
3554 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3559 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3560 ret = item->fState & MF_CHECKED;
3561 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3562 else item->fState &= ~MF_CHECKED;
3567 /**********************************************************************
3568 * EnableMenuItem (USER32.@)
3570 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3576 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3578 /* Get the Popupmenu to access the owner menu */
3579 if (!(menu = MENU_GetMenu(hMenu)))
3582 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3585 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3586 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3588 /* If the close item in the system menu change update the close button */
3589 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3591 if (menu->hSysMenuOwner != 0)
3594 POPUPMENU* parentMenu;
3596 /* Get the parent menu to access*/
3597 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3600 /* Refresh the frame to reflect the change */
3601 GetWindowRect(parentMenu->hWnd, &rc);
3602 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3604 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3612 /*******************************************************************
3613 * GetMenuStringA (USER32.@)
3615 INT WINAPI GetMenuStringA(
3616 HMENU hMenu, /* [in] menuhandle */
3617 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3618 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3619 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3620 UINT wFlags /* [in] MF_ flags */
3624 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3625 if (str && nMaxSiz) str[0] = '\0';
3626 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3627 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3630 if (!item->text) return 0;
3631 if (!str || !nMaxSiz) return strlenW(item->text);
3632 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3634 TRACE("returning %s\n", debugstr_a(str));
3639 /*******************************************************************
3640 * GetMenuStringW (USER32.@)
3642 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3643 LPWSTR str, INT nMaxSiz, UINT wFlags )
3647 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3648 if (str && nMaxSiz) str[0] = '\0';
3649 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3650 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3653 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3654 if( !(item->text)) {
3658 lstrcpynW( str, item->text, nMaxSiz );
3659 TRACE("returning %s\n", debugstr_w(str));
3660 return strlenW(str);
3664 /**********************************************************************
3665 * HiliteMenuItem (USER32.@)
3667 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3671 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3672 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3673 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3674 if (menu->FocusedItem == wItemID) return TRUE;
3675 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3676 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3681 /**********************************************************************
3682 * GetMenuState (USER32.@)
3684 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3687 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3688 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3689 debug_print_menuitem (" item: ", item, "");
3690 if (item->fType & MF_POPUP)
3692 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3693 if (!menu) return -1;
3694 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3698 /* We used to (from way back then) mask the result to 0xff. */
3699 /* I don't know why and it seems wrong as the documented */
3700 /* return flag MF_SEPARATOR is outside that mask. */
3701 return (item->fType | item->fState);
3706 /**********************************************************************
3707 * GetMenuItemCount (USER32.@)
3709 INT WINAPI GetMenuItemCount( HMENU hMenu )
3711 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3712 if (!menu) return -1;
3713 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3714 return menu->nItems;
3718 /**********************************************************************
3719 * GetMenuItemID (USER32.@)
3721 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3725 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3726 if (lpmi->fType & MF_POPUP) return -1;
3732 /*******************************************************************
3733 * InsertMenuW (USER32.@)
3735 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3736 UINT_PTR id, LPCWSTR str )
3740 if (IS_STRING_ITEM(flags) && str)
3741 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3742 hMenu, pos, flags, id, debugstr_w(str) );
3743 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3744 hMenu, pos, flags, id, str );
3746 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3748 if (!(MENU_SetItemData( item, flags, id, str )))
3750 RemoveMenu( hMenu, pos, flags );
3754 item->hCheckBit = item->hUnCheckBit = 0;
3759 /*******************************************************************
3760 * InsertMenuA (USER32.@)
3762 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3763 UINT_PTR id, LPCSTR str )
3767 if (IS_STRING_ITEM(flags) && str)
3769 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3770 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3773 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3774 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3775 HeapFree( GetProcessHeap(), 0, newstr );
3779 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3783 /*******************************************************************
3784 * AppendMenuA (USER32.@)
3786 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3787 UINT_PTR id, LPCSTR data )
3789 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3793 /*******************************************************************
3794 * AppendMenuW (USER32.@)
3796 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3797 UINT_PTR id, LPCWSTR data )
3799 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3803 /**********************************************************************
3804 * RemoveMenu (USER32.@)
3806 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3811 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3812 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3813 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3817 MENU_FreeItemData( item );
3819 if (--menu->nItems == 0)
3821 HeapFree( GetProcessHeap(), 0, menu->items );
3826 while(nPos < menu->nItems)
3832 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3833 menu->nItems * sizeof(MENUITEM) );
3839 /**********************************************************************
3840 * DeleteMenu (USER32.@)
3842 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3844 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3845 if (!item) return FALSE;
3846 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3847 /* nPos is now the position of the item */
3848 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3853 /*******************************************************************
3854 * ModifyMenuW (USER32.@)
3856 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3857 UINT_PTR id, LPCWSTR str )
3861 if (IS_STRING_ITEM(flags))
3862 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3864 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3866 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3867 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3868 return MENU_SetItemData( item, flags, id, str );
3872 /*******************************************************************
3873 * ModifyMenuA (USER32.@)
3875 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3876 UINT_PTR id, LPCSTR str )
3880 if (IS_STRING_ITEM(flags) && str)
3882 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3883 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3886 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3887 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3888 HeapFree( GetProcessHeap(), 0, newstr );
3892 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3896 /**********************************************************************
3897 * CreatePopupMenu (USER32.@)
3899 HMENU WINAPI CreatePopupMenu(void)
3904 if (!(hmenu = CreateMenu())) return 0;
3905 menu = MENU_GetMenu( hmenu );
3906 menu->wFlags |= MF_POPUP;
3907 menu->bTimeToHide = FALSE;
3912 /**********************************************************************
3913 * GetMenuCheckMarkDimensions (USER.417)
3914 * GetMenuCheckMarkDimensions (USER32.@)
3916 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3918 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3922 /**********************************************************************
3923 * SetMenuItemBitmaps (USER32.@)
3925 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3926 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3930 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3932 if (!hNewCheck && !hNewUnCheck)
3934 item->fState &= ~MF_USECHECKBITMAPS;
3936 else /* Install new bitmaps */
3938 item->hCheckBit = hNewCheck;
3939 item->hUnCheckBit = hNewUnCheck;
3940 item->fState |= MF_USECHECKBITMAPS;
3946 /**********************************************************************
3947 * CreateMenu (USER32.@)
3949 HMENU WINAPI CreateMenu(void)
3953 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3954 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3956 ZeroMemory(menu, sizeof(POPUPMENU));
3957 menu->wMagic = MENU_MAGIC;
3958 menu->FocusedItem = NO_SELECTED_ITEM;
3959 menu->bTimeToHide = FALSE;
3961 TRACE("return %p\n", hMenu );
3967 /**********************************************************************
3968 * DestroyMenu (USER32.@)
3970 BOOL WINAPI DestroyMenu( HMENU hMenu )
3972 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3974 TRACE("(%p)\n", hMenu);
3977 if (!lppop) return FALSE;
3979 lppop->wMagic = 0; /* Mark it as destroyed */
3981 /* DestroyMenu should not destroy system menu popup owner */
3982 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3984 DestroyWindow( lppop->hWnd );
3988 if (lppop->items) /* recursively destroy submenus */
3991 MENUITEM *item = lppop->items;
3992 for (i = lppop->nItems; i > 0; i--, item++)
3994 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3995 MENU_FreeItemData( item );
3997 HeapFree( GetProcessHeap(), 0, lppop->items );
3999 USER_HEAP_FREE( hMenu );
4004 /**********************************************************************
4005 * GetSystemMenu (USER32.@)
4007 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4009 WND *wndPtr = WIN_GetPtr( hWnd );
4012 if (wndPtr == WND_DESKTOP) return 0;
4013 if (wndPtr == WND_OTHER_PROCESS)
4015 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4019 if (wndPtr->hSysMenu && bRevert)
4021 DestroyMenu(wndPtr->hSysMenu);
4022 wndPtr->hSysMenu = 0;
4025 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4026 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4028 if( wndPtr->hSysMenu )
4031 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4033 /* Store the dummy sysmenu handle to facilitate the refresh */
4034 /* of the close button if the SC_CLOSE item change */
4035 menu = MENU_GetMenu(retvalue);
4037 menu->hSysMenuOwner = wndPtr->hSysMenu;
4039 WIN_ReleasePtr( wndPtr );
4041 return bRevert ? 0 : retvalue;
4045 /*******************************************************************
4046 * SetSystemMenu (USER32.@)
4048 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4050 WND *wndPtr = WIN_GetPtr( hwnd );
4052 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4054 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4055 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4056 WIN_ReleasePtr( wndPtr );
4063 /**********************************************************************
4064 * GetMenu (USER32.@)
4066 HMENU WINAPI GetMenu( HWND hWnd )
4068 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4069 TRACE("for %p returning %p\n", hWnd, retvalue);
4073 /**********************************************************************
4074 * GetMenuBarInfo (USER32.@)
4076 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4078 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4082 /**********************************************************************
4085 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4086 * SetWindowPos call that would result if SetMenu were called directly.
4088 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4090 TRACE("(%p, %p);\n", hWnd, hMenu);
4092 if (hMenu && !IsMenu(hMenu))
4094 WARN("hMenu %p is not a menu handle\n", hMenu);
4097 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4100 hWnd = WIN_GetFullHandle( hWnd );
4101 if (GetCapture() == hWnd)
4102 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4108 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4110 lpmenu->hWnd = hWnd;
4111 lpmenu->Height = 0; /* Make sure we recalculate the size */
4113 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4118 /**********************************************************************
4119 * SetMenu (USER32.@)
4121 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4123 if(!MENU_SetMenu(hWnd, hMenu))
4126 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4127 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4132 /**********************************************************************
4133 * GetSubMenu (USER32.@)
4135 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4139 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4140 if (!(lpmi->fType & MF_POPUP)) return 0;
4141 return lpmi->hSubMenu;
4145 /**********************************************************************
4146 * DrawMenuBar (USER32.@)
4148 BOOL WINAPI DrawMenuBar( HWND hWnd )
4151 HMENU hMenu = GetMenu(hWnd);
4153 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4155 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4157 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4158 lppop->hwndOwner = hWnd;
4159 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4160 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4164 /***********************************************************************
4165 * DrawMenuBarTemp (USER32.@)
4169 * called by W98SE desk.cpl Control Panel Applet
4171 * Not 100% sure about the param names, but close.
4173 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4178 BOOL flat_menu = FALSE;
4180 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4183 hMenu = GetMenu(hwnd);
4186 hFont = get_menu_font(FALSE);
4188 lppop = MENU_GetMenu( hMenu );
4189 if (lppop == NULL || lprect == NULL)
4191 retvalue = GetSystemMetrics(SM_CYMENU);
4195 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4197 hfontOld = SelectObject( hDC, hFont);
4199 if (lppop->Height == 0)
4200 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4202 lprect->bottom = lprect->top + lppop->Height;
4204 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4206 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4207 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4208 LineTo( hDC, lprect->right, lprect->bottom );
4210 if (lppop->nItems == 0)
4212 retvalue = GetSystemMetrics(SM_CYMENU);
4216 for (i = 0; i < lppop->nItems; i++)
4218 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4219 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4221 retvalue = lppop->Height;
4224 if (hfontOld) SelectObject (hDC, hfontOld);
4228 /***********************************************************************
4229 * EndMenu (USER.187)
4230 * EndMenu (USER32.@)
4232 BOOL WINAPI EndMenu(void)
4234 /* if we are in the menu code, and it is active */
4235 if (!fEndMenu && top_popup)
4237 /* terminate the menu handling code */
4240 /* needs to be posted to wakeup the internal menu handler */
4241 /* which will now terminate the menu, in the event that */
4242 /* the main window was minimized, or lost focus, so we */
4243 /* don't end up with an orphaned menu */
4244 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4250 /***********************************************************************
4251 * LookupMenuHandle (USER.217)
4253 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4255 HMENU hmenu32 = HMENU_32(hmenu);
4257 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4258 else return HMENU_16(hmenu32);
4262 /**********************************************************************
4263 * LoadMenu (USER.150)
4265 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4271 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4272 if (!name) return 0;
4274 instance = GetExePtr( instance );
4275 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4276 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4277 hMenu = LoadMenuIndirect16(LockResource16(handle));
4278 FreeResource16( handle );
4283 /*****************************************************************
4284 * LoadMenuA (USER32.@)
4286 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4288 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4289 if (!hrsrc) return 0;
4290 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4294 /*****************************************************************
4295 * LoadMenuW (USER32.@)
4297 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4299 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4300 if (!hrsrc) return 0;
4301 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4305 /**********************************************************************
4306 * LoadMenuIndirect (USER.220)
4308 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4311 WORD version, offset;
4312 LPCSTR p = (LPCSTR)template;
4314 TRACE("(%p)\n", template );
4315 version = GET_WORD(p);
4319 WARN("version must be 0 for Win16\n" );
4322 offset = GET_WORD(p);
4323 p += sizeof(WORD) + offset;
4324 if (!(hMenu = CreateMenu())) return 0;
4325 if (!MENU_ParseResource( p, hMenu, FALSE ))
4327 DestroyMenu( hMenu );
4330 return HMENU_16(hMenu);
4334 /**********************************************************************
4335 * LoadMenuIndirectW (USER32.@)
4337 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4340 WORD version, offset;
4341 LPCSTR p = (LPCSTR)template;
4343 version = GET_WORD(p);
4345 TRACE("%p, ver %d\n", template, version );
4348 case 0: /* standard format is version of 0 */
4349 offset = GET_WORD(p);
4350 p += sizeof(WORD) + offset;
4351 if (!(hMenu = CreateMenu())) return 0;
4352 if (!MENU_ParseResource( p, hMenu, TRUE ))
4354 DestroyMenu( hMenu );
4358 case 1: /* extended format is version of 1 */
4359 offset = GET_WORD(p);
4360 p += sizeof(WORD) + offset;
4361 if (!(hMenu = CreateMenu())) return 0;
4362 if (!MENUEX_ParseResource( p, hMenu))
4364 DestroyMenu( hMenu );
4369 ERR("version %d not supported.\n", version);
4375 /**********************************************************************
4376 * LoadMenuIndirectA (USER32.@)
4378 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4380 return LoadMenuIndirectW( template );
4384 /**********************************************************************
4387 BOOL WINAPI IsMenu(HMENU hmenu)
4389 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4393 SetLastError(ERROR_INVALID_MENU_HANDLE);
4399 /**********************************************************************
4400 * GetMenuItemInfo_common
4403 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4404 LPMENUITEMINFOW lpmii, BOOL unicode)
4406 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4408 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4413 if( lpmii->fMask & MIIM_TYPE) {
4414 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4415 WARN("invalid combination of fMask bits used\n");
4416 /* this does not happen on Win9x/ME */
4417 SetLastError( ERROR_INVALID_PARAMETER);
4420 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4421 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4422 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4423 if( lpmii->fType & MFT_BITMAP) {
4424 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4426 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4427 /* this does not happen on Win9x/ME */
4428 lpmii->dwTypeData = 0;
4433 /* copy the text string */
4434 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4436 if(lpmii->dwTypeData && lpmii->cch) {
4439 *((WCHAR *)lpmii->dwTypeData) = 0;
4441 *((CHAR *)lpmii->dwTypeData) = 0;
4447 len = strlenW(menu->text);
4448 if(lpmii->dwTypeData && lpmii->cch)
4449 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4453 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4454 0, NULL, NULL ) - 1;
4455 if(lpmii->dwTypeData && lpmii->cch)
4456 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4457 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4458 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4460 /* if we've copied a substring we return its length */
4461 if(lpmii->dwTypeData && lpmii->cch)
4462 if (lpmii->cch <= len + 1)
4467 /* return length of string */
4468 /* not on Win9x/ME if fType & MFT_BITMAP */
4474 if (lpmii->fMask & MIIM_FTYPE)
4475 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4477 if (lpmii->fMask & MIIM_BITMAP)
4478 lpmii->hbmpItem = menu->hbmpItem;
4480 if (lpmii->fMask & MIIM_STATE)
4481 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4483 if (lpmii->fMask & MIIM_ID)
4484 lpmii->wID = menu->wID;
4486 if (lpmii->fMask & MIIM_SUBMENU)
4487 lpmii->hSubMenu = menu->hSubMenu;
4489 /* hSubMenu is always cleared
4490 * (not on Win9x/ME ) */
4491 lpmii->hSubMenu = 0;
4494 if (lpmii->fMask & MIIM_CHECKMARKS) {
4495 lpmii->hbmpChecked = menu->hCheckBit;
4496 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4498 if (lpmii->fMask & MIIM_DATA)
4499 lpmii->dwItemData = menu->dwItemData;
4504 /**********************************************************************
4505 * GetMenuItemInfoA (USER32.@)
4507 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4508 LPMENUITEMINFOA lpmii)
4512 if( lpmii->cbSize != sizeof( mii) &&
4513 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4514 SetLastError( ERROR_INVALID_PARAMETER);
4517 memcpy( &mii, lpmii, lpmii->cbSize);
4518 mii.cbSize = sizeof( mii);
4519 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4520 (LPMENUITEMINFOW)&mii, FALSE);
4521 mii.cbSize = lpmii->cbSize;
4522 memcpy( lpmii, &mii, mii.cbSize);
4526 /**********************************************************************
4527 * GetMenuItemInfoW (USER32.@)
4529 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4530 LPMENUITEMINFOW lpmii)
4534 if( lpmii->cbSize != sizeof( mii) &&
4535 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4536 SetLastError( ERROR_INVALID_PARAMETER);
4539 memcpy( &mii, lpmii, lpmii->cbSize);
4540 mii.cbSize = sizeof( mii);
4541 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4542 mii.cbSize = lpmii->cbSize;
4543 memcpy( lpmii, &mii, mii.cbSize);
4548 /* set a menu item text from a ASCII or Unicode string */
4549 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4555 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4556 strcpyW( menu->text, text );
4560 LPCSTR str = (LPCSTR)text;
4561 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4562 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4563 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4568 /**********************************************************************
4569 * SetMenuItemInfo_common
4572 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4573 const MENUITEMINFOW *lpmii,
4576 if (!menu) return FALSE;
4578 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4580 if (lpmii->fMask & MIIM_TYPE ) {
4581 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4582 WARN("invalid combination of fMask bits used\n");
4583 /* this does not happen on Win9x/ME */
4584 SetLastError( ERROR_INVALID_PARAMETER);
4588 /* Remove the old type bits and replace them with the new ones */
4589 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4590 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4592 if (IS_STRING_ITEM(menu->fType)) {
4593 HeapFree(GetProcessHeap(), 0, menu->text);
4594 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4595 } else if( (menu->fType) & MFT_BITMAP)
4596 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4599 if (lpmii->fMask & MIIM_FTYPE ) {
4600 if(( lpmii->fType & MFT_BITMAP)) {
4601 SetLastError( ERROR_INVALID_PARAMETER);
4604 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4605 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4607 if (lpmii->fMask & MIIM_STRING ) {
4608 /* free the string when used */
4609 HeapFree(GetProcessHeap(), 0, menu->text);
4610 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4613 if (lpmii->fMask & MIIM_STATE)
4615 /* Other menu items having MFS_DEFAULT are not converted
4617 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4620 if (lpmii->fMask & MIIM_ID)
4621 menu->wID = lpmii->wID;
4623 if (lpmii->fMask & MIIM_SUBMENU) {
4624 menu->hSubMenu = lpmii->hSubMenu;
4625 if (menu->hSubMenu) {
4626 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4628 subMenu->wFlags |= MF_POPUP;
4629 menu->fType |= MF_POPUP;
4632 SetLastError( ERROR_INVALID_PARAMETER);
4637 menu->fType &= ~MF_POPUP;
4640 if (lpmii->fMask & MIIM_CHECKMARKS)
4642 menu->hCheckBit = lpmii->hbmpChecked;
4643 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4645 if (lpmii->fMask & MIIM_DATA)
4646 menu->dwItemData = lpmii->dwItemData;
4648 if (lpmii->fMask & MIIM_BITMAP)
4649 menu->hbmpItem = lpmii->hbmpItem;
4651 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4652 menu->fType |= MFT_SEPARATOR;
4654 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4658 /**********************************************************************
4659 * SetMenuItemInfoA (USER32.@)
4661 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4662 const MENUITEMINFOA *lpmii)
4666 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4668 if( lpmii->cbSize != sizeof( mii) &&
4669 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4670 SetLastError( ERROR_INVALID_PARAMETER);
4673 memcpy( &mii, lpmii, lpmii->cbSize);
4674 if( lpmii->cbSize != sizeof( mii)) {
4675 mii.cbSize = sizeof( mii);
4676 mii.hbmpItem = NULL;
4678 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4679 (const MENUITEMINFOW *)&mii, FALSE);
4682 /**********************************************************************
4683 * SetMenuItemInfoW (USER32.@)
4685 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4686 const MENUITEMINFOW *lpmii)
4690 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4692 if( lpmii->cbSize != sizeof( mii) &&
4693 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4694 SetLastError( ERROR_INVALID_PARAMETER);
4697 memcpy( &mii, lpmii, lpmii->cbSize);
4698 if( lpmii->cbSize != sizeof( mii)) {
4699 mii.cbSize = sizeof( mii);
4700 mii.hbmpItem = NULL;
4702 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4703 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4706 /**********************************************************************
4707 * SetMenuDefaultItem (USER32.@)
4710 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4716 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4718 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4720 /* reset all default-item flags */
4722 for (i = 0; i < menu->nItems; i++, item++)
4724 item->fState &= ~MFS_DEFAULT;
4727 /* no default item */
4736 if ( uItem >= menu->nItems ) return FALSE;
4737 item[uItem].fState |= MFS_DEFAULT;
4742 for (i = 0; i < menu->nItems; i++, item++)
4744 if (item->wID == uItem)
4746 item->fState |= MFS_DEFAULT;
4755 /**********************************************************************
4756 * GetMenuDefaultItem (USER32.@)
4758 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4764 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4766 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4768 /* find default item */
4772 if (! item) return -1;
4774 while ( !( item->fState & MFS_DEFAULT ) )
4777 if (i >= menu->nItems ) return -1;
4780 /* default: don't return disabled items */
4781 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4783 /* search rekursiv when needed */
4784 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4787 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4788 if ( -1 != ret ) return ret;
4790 /* when item not found in submenu, return the popup item */
4792 return ( bypos ) ? i : item->wID;
4797 /**********************************************************************
4798 * InsertMenuItemA (USER32.@)
4800 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4801 const MENUITEMINFOA *lpmii)
4806 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4808 if( lpmii->cbSize != sizeof( mii) &&
4809 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4810 SetLastError( ERROR_INVALID_PARAMETER);
4813 memcpy( &mii, lpmii, lpmii->cbSize);
4814 if( lpmii->cbSize != sizeof( mii)) {
4815 mii.cbSize = sizeof( mii);
4816 mii.hbmpItem = NULL;
4819 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4820 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4824 /**********************************************************************
4825 * InsertMenuItemW (USER32.@)
4827 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4828 const MENUITEMINFOW *lpmii)
4833 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4835 if( lpmii->cbSize != sizeof( mii) &&
4836 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4837 SetLastError( ERROR_INVALID_PARAMETER);
4840 memcpy( &mii, lpmii, lpmii->cbSize);
4841 if( lpmii->cbSize != sizeof( mii)) {
4842 mii.cbSize = sizeof( mii);
4843 mii.hbmpItem = NULL;
4846 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4847 return SetMenuItemInfo_common(item, &mii, TRUE);
4850 /**********************************************************************
4851 * CheckMenuRadioItem (USER32.@)
4854 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4855 UINT first, UINT last, UINT check,
4860 MENUITEM *mi_first = NULL, *mi_check;
4861 HMENU m_first, m_check;
4863 for (i = first; i <= last; i++)
4870 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4871 if (!mi_first) continue;
4872 mi_check = mi_first;
4878 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4879 if (!mi_check) continue;
4882 if (m_first != m_check) continue;
4883 if (mi_check->fType == MFT_SEPARATOR) continue;
4887 mi_check->fType |= MFT_RADIOCHECK;
4888 mi_check->fState |= MFS_CHECKED;
4893 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4894 mi_check->fState &= ~MFS_CHECKED;
4902 /**********************************************************************
4903 * GetMenuItemRect (USER32.@)
4905 * ATTENTION: Here, the returned values in rect are the screen
4906 * coordinates of the item just like if the menu was
4907 * always on the upper left side of the application.
4910 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4913 POPUPMENU *itemMenu;
4917 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4919 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4920 referenceHwnd = hwnd;
4924 itemMenu = MENU_GetMenu(hMenu);
4925 if (itemMenu == NULL)
4928 if(itemMenu->hWnd == 0)
4930 referenceHwnd = itemMenu->hWnd;
4933 if ((rect == NULL) || (item == NULL))
4938 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4944 /**********************************************************************
4945 * SetMenuInfo (USER32.@)
4948 * MIM_APPLYTOSUBMENUS
4949 * actually use the items to draw the menu
4951 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4955 TRACE("(%p %p)\n", hMenu, lpmi);
4957 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4960 if (lpmi->fMask & MIM_BACKGROUND)
4961 menu->hbrBack = lpmi->hbrBack;
4963 if (lpmi->fMask & MIM_HELPID)
4964 menu->dwContextHelpID = lpmi->dwContextHelpID;
4966 if (lpmi->fMask & MIM_MAXHEIGHT)
4967 menu->cyMax = lpmi->cyMax;
4969 if (lpmi->fMask & MIM_MENUDATA)
4970 menu->dwMenuData = lpmi->dwMenuData;
4972 if (lpmi->fMask & MIM_STYLE)
4974 menu->dwStyle = lpmi->dwStyle;
4975 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4976 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4977 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4978 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4986 /**********************************************************************
4987 * GetMenuInfo (USER32.@)
4993 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4996 TRACE("(%p %p)\n", hMenu, lpmi);
4998 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5001 if (lpmi->fMask & MIM_BACKGROUND)
5002 lpmi->hbrBack = menu->hbrBack;
5004 if (lpmi->fMask & MIM_HELPID)
5005 lpmi->dwContextHelpID = menu->dwContextHelpID;
5007 if (lpmi->fMask & MIM_MAXHEIGHT)
5008 lpmi->cyMax = menu->cyMax;
5010 if (lpmi->fMask & MIM_MENUDATA)
5011 lpmi->dwMenuData = menu->dwMenuData;
5013 if (lpmi->fMask & MIM_STYLE)
5014 lpmi->dwStyle = menu->dwStyle;
5022 /**********************************************************************
5023 * SetMenuContextHelpId (USER32.@)
5025 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5029 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5031 if ((menu = MENU_GetMenu(hMenu)))
5033 menu->dwContextHelpID = dwContextHelpID;
5040 /**********************************************************************
5041 * GetMenuContextHelpId (USER32.@)
5043 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5047 TRACE("(%p)\n", hMenu);
5049 if ((menu = MENU_GetMenu(hMenu)))
5051 return menu->dwContextHelpID;
5056 /**********************************************************************
5057 * MenuItemFromPoint (USER32.@)
5059 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5061 POPUPMENU *menu = MENU_GetMenu(hMenu);
5064 /*FIXME: Do we have to handle hWnd here? */
5065 if (!menu) return -1;
5066 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5071 /**********************************************************************
5072 * translate_accelerator
5074 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5075 BYTE fVirt, WORD key, WORD cmd )
5080 if (wParam != key) return FALSE;
5082 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5083 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5084 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5086 if (message == WM_CHAR || message == WM_SYSCHAR)
5088 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5090 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5096 if(fVirt & FVIRTKEY)
5098 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5099 wParam, 0xff & HIWORD(lParam));
5101 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5102 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5106 if (!(lParam & 0x01000000)) /* no special_key */
5108 if ((fVirt & FALT) && (lParam & 0x20000000))
5109 { /* ^^ ALT pressed */
5110 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5119 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5123 HMENU hMenu, hSubMenu, hSysMenu;
5124 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5126 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5127 hSysMenu = get_win_sys_menu( hWnd );
5129 /* find menu item and ask application to initialize it */
5130 /* 1. in the system menu */
5131 hSubMenu = hSysMenu;
5133 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5137 if (!IsWindowEnabled(hWnd))
5141 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5142 if(hSubMenu != hSysMenu)
5144 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5145 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5146 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5148 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5151 else /* 2. in the window's menu */
5155 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5159 if (!IsWindowEnabled(hWnd))
5163 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5164 if(hSubMenu != hMenu)
5166 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5167 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5168 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5170 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5177 if (uSysStat != (UINT)-1)
5179 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5186 if (uStat != (UINT)-1)
5192 if (uStat & (MF_DISABLED|MF_GRAYED))
5204 if( mesg==WM_COMMAND )
5206 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5207 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5209 else if( mesg==WM_SYSCOMMAND )
5211 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5212 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5216 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5217 * #0: unknown (please report!)
5218 * #1: for WM_KEYUP,WM_SYSKEYUP
5219 * #2: mouse is captured
5220 * #3: window is disabled
5221 * #4: it's a disabled system menu option
5222 * #5: it's a menu option, but window is iconic
5223 * #6: it's a menu option, but disabled
5225 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5227 ERR_(accel)(" unknown reason - please report!\n");
5232 /**********************************************************************
5233 * TranslateAcceleratorA (USER32.@)
5234 * TranslateAccelerator (USER32.@)
5236 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5239 LPACCEL16 lpAccelTbl;
5243 if (!hWnd || !msg) return 0;
5245 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5247 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5251 wParam = msg->wParam;
5253 switch (msg->message)
5262 char ch = LOWORD(wParam);
5264 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5265 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5273 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5274 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5278 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5279 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5281 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5286 /**********************************************************************
5287 * TranslateAcceleratorW (USER32.@)
5289 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5292 LPACCEL16 lpAccelTbl;
5295 if (!hWnd || !msg) return 0;
5297 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5299 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5303 switch (msg->message)
5315 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5316 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5320 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5321 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5323 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);