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.
44 #include "wine/port.h"
53 #include "wine/winbase16.h"
54 #include "wine/winuser16.h"
56 #include "wine/server.h"
57 #include "wine/unicode.h"
60 #include "user_private.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(menu);
64 WINE_DECLARE_DEBUG_CHANNEL(accel);
66 /* internal popup menu window messages */
68 #define MM_SETMENUHANDLE (WM_USER + 0)
69 #define MM_GETMENUHANDLE (WM_USER + 1)
71 /* Menu item structure */
73 /* ----------- MENUITEMINFO Stuff ----------- */
74 UINT fType; /* Item type. */
75 UINT fState; /* Item state. */
76 UINT_PTR wID; /* Item id. */
77 HMENU hSubMenu; /* Pop-up menu. */
78 HBITMAP hCheckBit; /* Bitmap when checked. */
79 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
80 LPWSTR text; /* Item text. */
81 ULONG_PTR dwItemData; /* Application defined. */
82 LPWSTR dwTypeData; /* depends on fMask */
83 HBITMAP hbmpItem; /* bitmap */
84 /* ----------- Wine stuff ----------- */
85 RECT rect; /* Item area (relative to menu window) */
86 UINT xTab; /* X position of text after Tab */
87 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
91 /* Popup menu structure */
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD wMagic; /* Magic number */
95 WORD Width; /* Width of the whole menu */
96 WORD Height; /* Height of the whole menu */
97 UINT nItems; /* Number of items in the menu */
98 HWND hWnd; /* Window containing the menu */
99 MENUITEM *items; /* Array of menu items */
100 UINT FocusedItem; /* Currently focused item */
101 HWND hwndOwner; /* window receiving the messages for ownerdraw */
102 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
103 BOOL bScrolling; /* Scroll arrows are active */
104 UINT nScrollPos; /* Current scroll position */
105 UINT nTotalHeight; /* Total height of menu items inside menu */
106 /* ------------ MENUINFO members ------ */
107 DWORD dwStyle; /* Extended menu style */
108 UINT cyMax; /* max height of the whole menu, 0 is screen height */
109 HBRUSH hbrBack; /* brush for menu background */
110 DWORD dwContextHelpID;
111 DWORD dwMenuData; /* application defined value */
112 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
113 SIZE maxBmpSize; /* Maximum size of the bitmap items */
114 } POPUPMENU, *LPPOPUPMENU;
116 /* internal flags for menu tracking */
118 #define TF_ENDMENU 0x0001
119 #define TF_SUSPENDPOPUP 0x0002
120 #define TF_SKIPREMOVE 0x0004
125 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
126 HMENU hTopMenu; /* initial menu */
127 HWND hOwnerWnd; /* where notifications are sent */
131 #define MENU_MAGIC 0x554d /* 'MU' */
136 /* Internal MENU_TrackMenu() flags */
137 #define TPM_INTERNAL 0xF0000000
138 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
139 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
140 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
142 /* Space between 2 columns */
143 #define MENU_COL_SPACE 4
145 /* top and bottom margins for popup menus */
146 #define MENU_TOP_MARGIN 3
147 #define MENU_BOTTOM_MARGIN 2
149 /* (other menu->FocusedItem values give the position of the focused item) */
150 #define NO_SELECTED_ITEM 0xffff
152 #define MENU_ITEM_TYPE(flags) \
153 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
156 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
157 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159 #define IS_SYSTEM_MENU(menu) \
160 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
165 MF_POPUP | MF_SYSMENU | MF_HELP)
166 #define STATE_MASK (~TYPE_MASK)
168 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
170 static SIZE menucharsize;
171 static UINT ODitemheight; /* default owner drawn item height */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
182 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 POPUPMENU_CLASS_ATOMA, /* name */
190 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
191 NULL, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc, /* procW */
193 sizeof(HMENU), /* extra */
194 IDC_ARROW, /* cursor */
195 (HBRUSH)(COLOR_MENU+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
208 #define MENUOUT(text) \
209 TRACE("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
216 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
219 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
220 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
221 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
222 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
223 TRACE("%s ", prefix);
225 UINT flags = mp->fType;
226 TRACE( "{ ID=0x%x", mp->wID);
228 TRACE( ", Sub=%p", mp->hSubMenu);
232 MENUFLAG( MFT_SEPARATOR, "sep");
233 MENUFLAG( MFT_OWNERDRAW, "own");
234 MENUFLAG( MFT_BITMAP, "bit");
235 MENUFLAG(MF_POPUP, "pop");
236 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
237 MENUFLAG(MFT_MENUBREAK, "brk");
238 MENUFLAG(MFT_RADIOCHECK, "radio");
239 MENUFLAG(MFT_RIGHTORDER, "rorder");
240 MENUFLAG(MF_SYSMENU, "sys");
241 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
243 TRACE( "+0x%x", flags);
249 MENUFLAG(MFS_GRAYED, "grey");
250 MENUFLAG(MFS_DEFAULT, "default");
251 MENUFLAG(MFS_DISABLED, "dis");
252 MENUFLAG(MFS_CHECKED, "check");
253 MENUFLAG(MFS_HILITE, "hi");
254 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
255 MENUFLAG(MF_MOUSESELECT, "mouse");
257 TRACE( "+0x%x", flags);
260 TRACE( ", Chk=%p", mp->hCheckBit);
262 TRACE( ", Unc=%p", mp->hUnCheckBit);
264 TRACE( ", Text=%s", debugstr_w(mp->text));
266 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
269 if( IS_MAGIC_BITMAP(mp->hbmpItem))
270 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
272 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE(" %s\n", postfix);
284 /***********************************************************************
287 * Validate the given menu handle and returns the menu structure pointer.
289 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
291 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
292 if (!menu || menu->wMagic != MENU_MAGIC)
294 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
300 /***********************************************************************
303 * Get the system menu of a window
305 static HMENU get_win_sys_menu( HWND hwnd )
308 WND *win = WIN_GetPtr( hwnd );
309 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 WIN_ReleasePtr( win );
317 /***********************************************************************
320 static HFONT get_menu_font( BOOL bold )
322 static HFONT hMenuFont, hMenuFontBold;
324 HFONT ret = bold ? hMenuFontBold : hMenuFont;
328 NONCLIENTMETRICSW ncm;
331 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
332 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
336 ncm.lfMenuFont.lfWeight += 300;
337 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
339 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
340 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 /* another thread beat us to it */
352 /***********************************************************************
355 static HBITMAP get_arrow_bitmap(void)
357 static HBITMAP arrow_bitmap;
359 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 /***********************************************************************
364 * get_down_arrow_bitmap
366 static HBITMAP get_down_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 /***********************************************************************
375 * get_down_arrow_inactive_bitmap
377 static HBITMAP get_down_arrow_inactive_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 /***********************************************************************
386 * get_up_arrow_bitmap
388 static HBITMAP get_up_arrow_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 /***********************************************************************
397 * get_up_arrow_inactive_bitmap
399 static HBITMAP get_up_arrow_inactive_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 /***********************************************************************
410 * Return the default system menu.
412 static HMENU MENU_CopySysPopup(void)
414 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
415 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
418 POPUPMENU* menu = MENU_GetMenu(hMenu);
419 menu->wFlags |= MF_SYSMENU | MF_POPUP;
420 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
423 ERR("Unable to load default system menu\n" );
425 TRACE("returning %p.\n", hMenu );
431 /**********************************************************************
434 * Create a copy of the system menu. System menu in Windows is
435 * a special menu bar with the single entry - system menu popup.
436 * This popup is presented to the outside world as a "system menu".
437 * However, the real system menu handle is sometimes seen in the
438 * WM_MENUSELECT parameters (and Word 6 likes it this way).
440 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
444 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
445 if ((hMenu = CreateMenu()))
447 POPUPMENU *menu = MENU_GetMenu(hMenu);
448 menu->wFlags = MF_SYSMENU;
449 menu->hWnd = WIN_GetFullHandle( hWnd );
450 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
453 hPopupMenu = MENU_CopySysPopup();
457 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
458 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
460 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
461 (UINT_PTR)hPopupMenu, NULL );
463 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
464 menu->items[0].fState = 0;
465 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
467 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
470 DestroyMenu( hMenu );
472 ERR("failed to load system menu!\n");
477 /***********************************************************************
478 * MENU_InitSysMenuPopup
480 * Grey the appropriate items in System menu.
482 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
486 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
487 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
488 gray = ((style & WS_MAXIMIZE) != 0);
489 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
490 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
491 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
493 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
495 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = (clsStyle & CS_NOCLOSE) != 0;
498 /* The menu item must keep its state if it's disabled */
500 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
504 /******************************************************************************
506 * UINT MENU_GetStartOfNextColumn(
509 *****************************************************************************/
511 static UINT MENU_GetStartOfNextColumn(
514 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 return NO_SELECTED_ITEM;
520 i = menu->FocusedItem + 1;
521 if( i == NO_SELECTED_ITEM )
524 for( ; i < menu->nItems; ++i ) {
525 if (menu->items[i].fType & MF_MENUBARBREAK)
529 return NO_SELECTED_ITEM;
533 /******************************************************************************
535 * UINT MENU_GetStartOfPrevColumn(
538 *****************************************************************************/
540 static UINT MENU_GetStartOfPrevColumn(
543 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 return NO_SELECTED_ITEM;
549 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
550 return NO_SELECTED_ITEM;
552 /* Find the start of the column */
554 for(i = menu->FocusedItem; i != 0 &&
555 !(menu->items[i].fType & MF_MENUBARBREAK);
559 return NO_SELECTED_ITEM;
561 for(--i; i != 0; --i) {
562 if (menu->items[i].fType & MF_MENUBARBREAK)
566 TRACE("ret %d.\n", i );
573 /***********************************************************************
576 * Find a menu item. Return a pointer on the item, and modifies *hmenu
577 * in case the item was in a sub-menu.
579 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
582 MENUITEM *fallback = NULL;
583 UINT fallback_pos = 0;
586 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
587 if (wFlags & MF_BYPOSITION)
589 if (*nPos >= menu->nItems) return NULL;
590 return &menu->items[*nPos];
594 MENUITEM *item = menu->items;
595 for (i = 0; i < menu->nItems; i++, item++)
597 if (item->fType & MF_POPUP)
599 HMENU hsubmenu = item->hSubMenu;
600 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
606 else if (item->wID == *nPos)
608 /* fallback to this item if nothing else found */
613 else if (item->wID == *nPos)
622 *nPos = fallback_pos;
627 /***********************************************************************
630 * Find a Sub menu. Return the position of the submenu, and modifies
631 * *hmenu in case it is found in another sub-menu.
632 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
634 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
639 if (((*hmenu)==(HMENU)0xffff) ||
640 (!(menu = MENU_GetMenu(*hmenu))))
641 return NO_SELECTED_ITEM;
643 for (i = 0; i < menu->nItems; i++, item++) {
644 if(!(item->fType & MF_POPUP)) continue;
645 if (item->hSubMenu == hSubTarget) {
649 HMENU hsubmenu = item->hSubMenu;
650 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
651 if (pos != NO_SELECTED_ITEM) {
657 return NO_SELECTED_ITEM;
660 /***********************************************************************
663 static void MENU_FreeItemData( MENUITEM* item )
666 HeapFree( GetProcessHeap(), 0, item->text );
669 /***********************************************************************
670 * MENU_AdjustMenuItemRect
672 * Adjust menu item rectangle according to scrolling state.
675 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
677 if (menu->bScrolling)
679 UINT arrow_bitmap_width, arrow_bitmap_height;
682 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
683 arrow_bitmap_width = bmp.bmWidth;
684 arrow_bitmap_height = bmp.bmHeight;
685 rect->top += arrow_bitmap_height - menu->nScrollPos;
686 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
691 /***********************************************************************
692 * MENU_FindItemByCoords
694 * Find the item at the specified coordinates (screen coords). Does
695 * not work for child windows and therefore should not be called for
696 * an arbitrary system menu.
698 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
699 POINT pt, UINT *pos )
706 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
707 pt.x -= wrect.left;pt.y -= wrect.top;
709 for (i = 0; i < menu->nItems; i++, item++)
712 MENU_AdjustMenuItemRect(menu, &rect);
713 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
714 (pt.y >= rect.top) && (pt.y < rect.bottom))
724 /***********************************************************************
727 * Find the menu item selected by a key press.
728 * Return item id, -1 if none, -2 if we should close the menu.
730 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
731 WCHAR key, BOOL forceMenuChar )
733 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
735 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
739 POPUPMENU *menu = MENU_GetMenu( hmenu );
740 MENUITEM *item = menu->items;
747 for (i = 0; i < menu->nItems; i++, item++)
751 WCHAR *p = item->text - 2;
754 p = strchrW (p + 2, '&');
756 while (p != NULL && p [1] == '&');
757 if (p && (toupperW(p[1]) == toupperW(key))) return i;
761 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
762 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
763 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
764 if (HIWORD(menuchar) == 1) return (UINT)(-2);
770 /***********************************************************************
771 * MENU_GetBitmapItemSize
773 * Get the size of a bitmap item.
775 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
779 HBITMAP bmp = lpitem->hbmpItem;
781 size->cx = size->cy = 0;
783 /* check if there is a magic menu item associated with this item */
784 switch( (INT_PTR) bmp )
786 case (INT_PTR)HBMMENU_CALLBACK:
788 MEASUREITEMSTRUCT measItem;
789 measItem.CtlType = ODT_MENU;
791 measItem.itemID = lpitem->wID;
792 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
793 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
794 measItem.itemData = lpitem->dwItemData;
795 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
796 size->cx = measItem.itemWidth;
797 size->cy = measItem.itemHeight;
801 case (INT_PTR)HBMMENU_SYSTEM:
802 if (lpitem->dwItemData)
804 bmp = (HBITMAP)lpitem->dwItemData;
808 case (INT_PTR)HBMMENU_MBAR_RESTORE:
809 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
811 case (INT_PTR)HBMMENU_MBAR_CLOSE:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
813 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
816 case (INT_PTR)HBMMENU_POPUP_CLOSE:
817 case (INT_PTR)HBMMENU_POPUP_RESTORE:
818 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
819 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
820 FIXME("Magic %p not implemented\n", bmp );
823 if (GetObjectW(bmp, sizeof(bm), &bm ))
825 size->cx = bm.bmWidth;
826 size->cy = bm.bmHeight;
830 /***********************************************************************
831 * MENU_DrawBitmapItem
833 * Draw a bitmap item.
835 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
836 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
842 int w = rect->right - rect->left;
843 int h = rect->bottom - rect->top;
846 HBITMAP hbmToDraw = lpitem->hbmpItem;
849 /* Check if there is a magic menu item associated with this item */
850 if (IS_MAGIC_BITMAP(hbmToDraw))
855 switch((INT_PTR)hbmToDraw)
857 case (INT_PTR)HBMMENU_SYSTEM:
858 if (lpitem->dwItemData)
860 bmp = (HBITMAP)lpitem->dwItemData;
861 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
865 static HBITMAP hBmpSysMenu;
867 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
869 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
870 /* only use right half of the bitmap */
871 bmp_xoffset = bm.bmWidth / 2;
872 bm.bmWidth -= bmp_xoffset;
875 case (INT_PTR)HBMMENU_MBAR_RESTORE:
876 flags = DFCS_CAPTIONRESTORE;
878 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
879 flags = DFCS_CAPTIONMIN;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
882 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
884 case (INT_PTR)HBMMENU_MBAR_CLOSE:
885 flags = DFCS_CAPTIONCLOSE;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
888 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
890 case (INT_PTR)HBMMENU_CALLBACK:
892 DRAWITEMSTRUCT drawItem;
893 drawItem.CtlType = ODT_MENU;
895 drawItem.itemID = lpitem->wID;
896 drawItem.itemAction = odaction;
897 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
898 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
899 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
900 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
902 drawItem.hwndItem = (HWND)hmenu;
904 drawItem.itemData = lpitem->dwItemData;
905 drawItem.rcItem = *rect;
906 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
910 case (INT_PTR)HBMMENU_POPUP_CLOSE:
911 case (INT_PTR)HBMMENU_POPUP_RESTORE:
912 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
913 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
915 FIXME("Magic %p not implemented\n", hbmToDraw);
919 InflateRect( &r, -1, -1 );
920 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
921 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
925 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928 hdcMem = CreateCompatibleDC( hdc );
929 SelectObject( hdcMem, bmp );
931 /* handle fontsize > bitmap_height */
932 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
934 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
935 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
936 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
937 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
942 /***********************************************************************
945 * Calculate the size of the menu item and store it in lpitem->rect.
947 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
948 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
952 UINT arrow_bitmap_width;
956 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
957 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
958 (menuBar ? " (MenuBar)" : ""));
960 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
961 arrow_bitmap_width = bm.bmWidth;
963 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
964 if( !menucharsize.cx ) {
965 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
966 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
967 * but it is unlikely an application will depend on that */
968 ODitemheight = HIWORD( GetDialogBaseUnits());
971 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
973 if (lpitem->fType & MF_OWNERDRAW)
975 MEASUREITEMSTRUCT mis;
976 mis.CtlType = ODT_MENU;
978 mis.itemID = lpitem->wID;
979 mis.itemData = lpitem->dwItemData;
980 mis.itemHeight = ODitemheight;
982 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
983 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
984 * width of a menufont character to the width of an owner-drawn menu.
986 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
988 /* under at least win95 you seem to be given a standard
989 height for the menu and the height value is ignored */
990 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
992 lpitem->rect.bottom += mis.itemHeight;
994 TRACE("id=%04x size=%ldx%ld\n",
995 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
996 lpitem->rect.bottom-lpitem->rect.top);
1000 if (lpitem->fType & MF_SEPARATOR)
1002 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1004 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1012 if (lpitem->hbmpItem) {
1015 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1016 /* Keep the size of the bitmap in callback mode to be able
1017 * to draw it correctly */
1018 lpitem->bmpsize = size;
1019 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1020 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1021 lpitem->rect.right += size.cx + 2;
1022 itemheight = size.cy + 2;
1024 if( !(lppop->dwStyle & MNS_NOCHECK))
1025 lpitem->rect.right += check_bitmap_width;
1026 lpitem->rect.right += 4 + menucharsize.cx;
1027 lpitem->xTab = lpitem->rect.right;
1028 lpitem->rect.right += arrow_bitmap_width;
1029 } else if (lpitem->hbmpItem) { /* menuBar */
1032 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1033 lpitem->bmpsize = size;
1034 lpitem->rect.right += size.cx;
1035 if( lpitem->text) lpitem->rect.right += 2;
1036 itemheight = size.cy;
1039 /* it must be a text item - unless it's the system menu */
1040 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1041 HFONT hfontOld = NULL;
1042 RECT rc = lpitem->rect;
1043 LONG txtheight, txtwidth;
1045 if ( lpitem->fState & MFS_DEFAULT ) {
1046 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1050 DT_SINGLELINE|DT_CALCRECT);
1051 lpitem->rect.right += rc.right - rc.left;
1052 itemheight = max( max( itemheight, txtheight),
1053 GetSystemMetrics( SM_CYMENU) - 1);
1054 lpitem->rect.right += 2 * menucharsize.cx;
1056 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1059 int n = (int)( p - lpitem->text);
1060 /* Item contains a tab (only meaningful in popup menus) */
1061 /* get text size before the tab */
1062 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1063 DT_SINGLELINE|DT_CALCRECT);
1064 txtwidth = rc.right - rc.left;
1065 p += 1; /* advance past the Tab */
1066 /* get text size after the tab */
1067 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1068 DT_SINGLELINE|DT_CALCRECT);
1069 lpitem->xTab += txtwidth;
1070 txtheight = max( txtheight, tmpheight);
1071 txtwidth += menucharsize.cx + /* space for the tab */
1072 tmprc.right - tmprc.left; /* space for the short cut */
1074 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1075 DT_SINGLELINE|DT_CALCRECT);
1076 txtwidth = rc.right - rc.left;
1077 lpitem->xTab += txtwidth;
1079 lpitem->rect.right += 2 + txtwidth;
1080 itemheight = max( itemheight,
1081 max( txtheight + 2, menucharsize.cy + 4));
1083 if (hfontOld) SelectObject (hdc, hfontOld);
1084 } else if( menuBar) {
1085 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1087 lpitem->rect.bottom += itemheight;
1088 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1092 /***********************************************************************
1093 * MENU_GetMaxPopupHeight
1096 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1099 return lppop->cyMax;
1100 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1104 /***********************************************************************
1105 * MENU_PopupMenuCalcSize
1107 * Calculate the size of a popup menu.
1109 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1114 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1116 lppop->Width = lppop->Height = 0;
1117 if (lppop->nItems == 0) return;
1120 SelectObject( hdc, get_menu_font(FALSE));
1125 lppop->maxBmpSize.cx = 0;
1126 lppop->maxBmpSize.cy = 0;
1128 while (start < lppop->nItems)
1130 lpitem = &lppop->items[start];
1132 if( lpitem->fType & MF_MENUBREAK)
1133 orgX += MENU_COL_SPACE;
1134 orgY = MENU_TOP_MARGIN;
1136 maxTab = maxTabWidth = 0;
1137 /* Parse items until column break or end of menu */
1138 for (i = start; i < lppop->nItems; i++, lpitem++)
1141 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1143 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1145 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
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(LPPOPUPMENU 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))
1459 rc.bottom = height - 3;
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1479 rc.top = ( rc.top + rc.bottom) / 2;
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1524 INT y = rect.top + rect.bottom;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1589 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1776 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1777 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1779 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1780 if (menu->FocusedItem != NO_SELECTED_ITEM)
1782 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1783 menu->FocusedItem = NO_SELECTED_ITEM;
1786 /* store the owner for DrawItem */
1787 menu->hwndOwner = hwndOwner;
1789 menu->nScrollPos = 0;
1790 MENU_PopupMenuCalcSize( menu, hwndOwner );
1792 /* adjust popup menu pos so that it fits within the desktop */
1794 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1795 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1797 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1799 if( xanchor && x >= width - xanchor )
1800 x -= width - xanchor;
1802 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1803 x = GetSystemMetrics(SM_CXSCREEN) - width;
1807 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1809 if( yanchor && y >= height + yanchor )
1810 y -= height + yanchor;
1812 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1813 y = GetSystemMetrics(SM_CYSCREEN) - height;
1817 /* NOTE: In Windows, top menu popup is not owned. */
1818 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1819 WS_POPUP, x, y, width, height,
1820 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1822 if( !menu->hWnd ) return FALSE;
1823 if (!top_popup) top_popup = menu->hWnd;
1825 /* Display the window */
1827 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1828 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1829 UpdateWindow( menu->hWnd );
1834 /***********************************************************************
1835 * MENU_EnsureMenuItemVisible
1838 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1840 if (lppop->bScrolling)
1842 MENUITEM *item = &lppop->items[wIndex];
1843 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1844 UINT nOldPos = lppop->nScrollPos;
1846 UINT arrow_bitmap_height;
1849 GetClientRect(lppop->hWnd, &rc);
1851 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1852 arrow_bitmap_height = bmp.bmHeight;
1854 rc.top += arrow_bitmap_height;
1855 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1857 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1858 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1861 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1862 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1863 MENU_DrawScrollArrows(lppop, hdc);
1865 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1867 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1868 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1869 MENU_DrawScrollArrows(lppop, hdc);
1875 /***********************************************************************
1878 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1879 BOOL sendMenuSelect, HMENU topmenu )
1884 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1886 lppop = MENU_GetMenu( hmenu );
1887 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1889 if (lppop->FocusedItem == wIndex) return;
1890 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1891 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1892 if (!top_popup) top_popup = lppop->hWnd;
1894 SelectObject( hdc, get_menu_font(FALSE));
1896 /* Clear previous highlighted item */
1897 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1899 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1900 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1901 lppop->Height, !(lppop->wFlags & MF_POPUP),
1905 /* Highlight new item (if any) */
1906 lppop->FocusedItem = wIndex;
1907 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1909 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1910 lppop->items[wIndex].fState |= MF_HILITE;
1911 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1912 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1913 &lppop->items[wIndex], lppop->Height,
1914 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1918 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1919 SendMessageW( hwndOwner, WM_MENUSELECT,
1920 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1921 ip->fType | ip->fState |
1922 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1925 else if (sendMenuSelect) {
1928 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1929 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1930 MENUITEM *ip = &ptm->items[pos];
1931 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1932 ip->fType | ip->fState |
1933 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1937 ReleaseDC( lppop->hWnd, hdc );
1941 /***********************************************************************
1942 * MENU_MoveSelection
1944 * Moves currently selected item according to the offset parameter.
1945 * If there is no selection then it should select the last item if
1946 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1948 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1953 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1955 menu = MENU_GetMenu( hmenu );
1956 if ((!menu) || (!menu->items)) return;
1958 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1960 if( menu->nItems == 1 ) return; else
1961 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1963 if (!(menu->items[i].fType & MF_SEPARATOR))
1965 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1970 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1971 i >= 0 && i < menu->nItems ; i += offset)
1972 if (!(menu->items[i].fType & MF_SEPARATOR))
1974 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1980 /**********************************************************************
1983 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1986 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1989 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1990 TRACE("flags=%x str=%p\n", flags, str);
1992 if (IS_STRING_ITEM(flags))
1994 LPWSTR prevText = item->text;
1997 flags |= MF_SEPARATOR;
2003 /* Item beginning with a backspace is a help item */
2009 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2011 strcpyW( text, str );
2014 item->hbmpItem = NULL;
2015 HeapFree( GetProcessHeap(), 0, prevText );
2017 else if(( flags & MFT_BITMAP)) {
2018 item->hbmpItem = HBITMAP_32(LOWORD(str));
2019 /* setting bitmap clears text */
2020 HeapFree( GetProcessHeap(), 0, item->text );
2024 if (flags & MF_OWNERDRAW)
2025 item->dwItemData = (DWORD_PTR)str;
2027 item->dwItemData = 0;
2029 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2030 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2032 if (flags & MF_POPUP)
2034 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2035 if (menu) menu->wFlags |= MF_POPUP;
2047 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2049 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2050 flags |= MF_POPUP; /* keep popup */
2052 item->fType = flags & TYPE_MASK;
2053 item->fState = (flags & STATE_MASK) &
2054 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2056 /* Don't call SetRectEmpty here! */
2058 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2063 /**********************************************************************
2066 * Insert (allocate) a new item into a menu.
2068 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2073 if (!(menu = MENU_GetMenu(hMenu)))
2076 /* Find where to insert new item */
2078 if (flags & MF_BYPOSITION) {
2079 if (pos > menu->nItems)
2082 if (!MENU_FindItem( &hMenu, &pos, flags ))
2085 if (!(menu = MENU_GetMenu( hMenu )))
2090 /* Create new items array */
2092 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2095 WARN("allocation failed\n" );
2098 if (menu->nItems > 0)
2100 /* Copy the old array into the new one */
2101 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2102 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2103 (menu->nItems-pos)*sizeof(MENUITEM) );
2104 HeapFree( GetProcessHeap(), 0, menu->items );
2106 menu->items = newItems;
2108 memset( &newItems[pos], 0, sizeof(*newItems) );
2109 menu->Height = 0; /* force size recalculate */
2110 return &newItems[pos];
2114 /**********************************************************************
2115 * MENU_ParseResource
2117 * Parse a standard menu resource and add items to the menu.
2118 * Return a pointer to the end of the resource.
2120 * NOTE: flags is equivalent to the mtOption field
2122 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2129 flags = GET_WORD(res);
2130 res += sizeof(WORD);
2131 if (!(flags & MF_POPUP))
2134 res += sizeof(WORD);
2137 if (!unicode) res += strlen(str) + 1;
2138 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2139 if (flags & MF_POPUP)
2141 HMENU hSubMenu = CreatePopupMenu();
2142 if (!hSubMenu) return NULL;
2143 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2145 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2146 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2148 else /* Not a popup */
2150 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2151 else AppendMenuW( hMenu, flags, id,
2152 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2154 } while (!(flags & MF_END));
2159 /**********************************************************************
2160 * MENUEX_ParseResource
2162 * Parse an extended menu resource and add items to the menu.
2163 * Return a pointer to the end of the resource.
2165 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2171 mii.cbSize = sizeof(mii);
2172 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2173 mii.fType = GET_DWORD(res);
2174 res += sizeof(DWORD);
2175 mii.fState = GET_DWORD(res);
2176 res += sizeof(DWORD);
2177 mii.wID = GET_DWORD(res);
2178 res += sizeof(DWORD);
2179 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2180 res += sizeof(WORD);
2181 /* Align the text on a word boundary. */
2182 res += (~((UINT_PTR)res - 1)) & 1;
2183 mii.dwTypeData = (LPWSTR) res;
2184 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2185 /* Align the following fields on a dword boundary. */
2186 res += (~((UINT_PTR)res - 1)) & 3;
2188 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2189 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2191 if (resinfo & 1) { /* Pop-up? */
2192 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2193 res += sizeof(DWORD);
2194 mii.hSubMenu = CreatePopupMenu();
2197 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2198 DestroyMenu(mii.hSubMenu);
2201 mii.fMask |= MIIM_SUBMENU;
2202 mii.fType |= MF_POPUP;
2204 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2206 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2207 mii.wID, mii.fType);
2208 mii.fType |= MF_SEPARATOR;
2210 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2211 } while (!(resinfo & MF_END));
2216 /***********************************************************************
2219 * Return the handle of the selected sub-popup menu (if any).
2221 static HMENU MENU_GetSubPopup( HMENU hmenu )
2226 menu = MENU_GetMenu( hmenu );
2228 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2230 item = &menu->items[menu->FocusedItem];
2231 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2232 return item->hSubMenu;
2237 /***********************************************************************
2238 * MENU_HideSubPopups
2240 * Hide the sub-popup menus of this menu.
2242 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2243 BOOL sendMenuSelect )
2245 POPUPMENU *menu = MENU_GetMenu( hmenu );
2247 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2249 if (menu && top_popup)
2255 if (menu->FocusedItem != NO_SELECTED_ITEM)
2257 item = &menu->items[menu->FocusedItem];
2258 if (!(item->fType & MF_POPUP) ||
2259 !(item->fState & MF_MOUSESELECT)) return;
2260 item->fState &= ~MF_MOUSESELECT;
2261 hsubmenu = item->hSubMenu;
2264 submenu = MENU_GetMenu( hsubmenu );
2265 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2266 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2267 DestroyWindow( submenu->hWnd );
2273 /***********************************************************************
2276 * Display the sub-menu of the selected item of this menu.
2277 * Return the handle of the submenu, or hmenu if no submenu to display.
2279 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2280 BOOL selectFirst, UINT wFlags )
2287 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2289 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2291 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2293 item = &menu->items[menu->FocusedItem];
2294 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2297 /* message must be sent before using item,
2298 because nearly everything may be changed by the application ! */
2300 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2301 if (!(wFlags & TPM_NONOTIFY))
2302 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2303 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2305 item = &menu->items[menu->FocusedItem];
2308 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2309 if (!(item->fState & MF_HILITE))
2311 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2312 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2314 SelectObject( hdc, get_menu_font(FALSE));
2316 item->fState |= MF_HILITE;
2317 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2318 ReleaseDC( menu->hWnd, hdc );
2320 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2323 item->fState |= MF_MOUSESELECT;
2325 if (IS_SYSTEM_MENU(menu))
2327 MENU_InitSysMenuPopup(item->hSubMenu,
2328 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2329 GetClassLongW( menu->hWnd, GCL_STYLE));
2331 NC_GetSysPopupPos( menu->hWnd, &rect );
2332 rect.top = rect.bottom;
2333 rect.right = GetSystemMetrics(SM_CXSIZE);
2334 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2338 GetWindowRect( menu->hWnd, &rect );
2339 if (menu->wFlags & MF_POPUP)
2341 RECT rc = item->rect;
2343 MENU_AdjustMenuItemRect(menu, &rc);
2345 /* The first item in the popup menu has to be at the
2346 same y position as the focused menu item */
2347 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2348 rect.top += rc.top - MENU_TOP_MARGIN;
2349 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2350 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2351 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2355 rect.left += item->rect.left;
2356 rect.top += item->rect.bottom;
2357 rect.right = item->rect.right - item->rect.left;
2358 rect.bottom = item->rect.bottom - item->rect.top;
2362 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2363 rect.left, rect.top, rect.right, rect.bottom );
2365 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2366 return item->hSubMenu;
2371 /**********************************************************************
2374 HWND MENU_IsMenuActive(void)
2379 /***********************************************************************
2382 * Walks menu chain trying to find a menu pt maps to.
2384 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2386 POPUPMENU *menu = MENU_GetMenu( hMenu );
2387 UINT item = menu->FocusedItem;
2390 /* try subpopup first (if any) */
2391 ret = (item != NO_SELECTED_ITEM &&
2392 (menu->items[item].fType & MF_POPUP) &&
2393 (menu->items[item].fState & MF_MOUSESELECT))
2394 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2396 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2398 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2399 if( menu->wFlags & MF_POPUP )
2401 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2403 else if (ht == HTSYSMENU)
2404 ret = get_win_sys_menu( menu->hWnd );
2405 else if (ht == HTMENU)
2406 ret = GetMenu( menu->hWnd );
2411 /***********************************************************************
2412 * MENU_ExecFocusedItem
2414 * Execute a menu item (for instance when user pressed Enter).
2415 * Return the wID of the executed item. Otherwise, -1 indicating
2416 * that no menu item was executed, -2 if a popup is shown;
2417 * Have to receive the flags for the TrackPopupMenu options to avoid
2418 * sending unwanted message.
2421 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2424 POPUPMENU *menu = MENU_GetMenu( hMenu );
2426 TRACE("%p hmenu=%p\n", pmt, hMenu);
2428 if (!menu || !menu->nItems ||
2429 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2431 item = &menu->items[menu->FocusedItem];
2433 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2435 if (!(item->fType & MF_POPUP))
2437 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2439 /* If TPM_RETURNCMD is set you return the id, but
2440 do not send a message to the owner */
2441 if(!(wFlags & TPM_RETURNCMD))
2443 if( menu->wFlags & MF_SYSMENU )
2444 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2445 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2447 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2454 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2461 /***********************************************************************
2462 * MENU_SwitchTracking
2464 * Helper function for menu navigation routines.
2466 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2468 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2469 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2471 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2473 if( pmt->hTopMenu != hPtMenu &&
2474 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2476 /* both are top level menus (system and menu-bar) */
2477 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2478 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2479 pmt->hTopMenu = hPtMenu;
2481 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2482 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2486 /***********************************************************************
2489 * Return TRUE if we can go on with menu tracking.
2491 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2493 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2498 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2501 if( IS_SYSTEM_MENU(ptmenu) )
2502 item = ptmenu->items;
2504 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2508 if( ptmenu->FocusedItem != id )
2509 MENU_SwitchTracking( pmt, hPtMenu, id );
2511 /* If the popup menu is not already "popped" */
2512 if(!(item->fState & MF_MOUSESELECT ))
2514 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2519 /* Else the click was on the menu bar, finish the tracking */
2524 /***********************************************************************
2527 * Return the value of MENU_ExecFocusedItem if
2528 * the selected item was not a popup. Else open the popup.
2529 * A -1 return value indicates that we go on with menu tracking.
2532 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2534 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2539 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2542 if( IS_SYSTEM_MENU(ptmenu) )
2543 item = ptmenu->items;
2545 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2547 if( item && (ptmenu->FocusedItem == id ))
2549 if( !(item->fType & MF_POPUP) )
2551 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2552 return (executedMenuId < 0) ? -1 : executedMenuId;
2555 /* If we are dealing with the top-level menu */
2556 /* and this is a click on an already "popped" item: */
2557 /* Stop the menu tracking and close the opened submenus */
2558 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2561 ptmenu->bTimeToHide = TRUE;
2567 /***********************************************************************
2570 * Return TRUE if we can go on with menu tracking.
2572 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2574 UINT id = NO_SELECTED_ITEM;
2575 POPUPMENU *ptmenu = NULL;
2579 ptmenu = MENU_GetMenu( hPtMenu );
2580 if( IS_SYSTEM_MENU(ptmenu) )
2583 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2586 if( id == NO_SELECTED_ITEM )
2588 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2589 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2592 else if( ptmenu->FocusedItem != id )
2594 MENU_SwitchTracking( pmt, hPtMenu, id );
2595 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2601 /***********************************************************************
2604 static void MENU_SetCapture( HWND hwnd )
2608 SERVER_START_REQ( set_capture_window )
2611 req->flags = CAPTURE_MENU;
2612 if (!wine_server_call_err( req ))
2614 previous = reply->previous;
2615 hwnd = reply->full_handle;
2620 if (previous && previous != hwnd)
2621 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2625 /***********************************************************************
2628 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2630 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2632 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2634 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2635 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2637 MDINEXTMENU next_menu;
2642 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2643 next_menu.hmenuNext = 0;
2644 next_menu.hwndNext = 0;
2645 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2647 TRACE("%p [%p] -> %p [%p]\n",
2648 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2650 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2652 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2653 hNewWnd = pmt->hOwnerWnd;
2654 if( IS_SYSTEM_MENU(menu) )
2656 /* switch to the menu bar */
2658 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2662 menu = MENU_GetMenu( hNewMenu );
2663 id = menu->nItems - 1;
2666 else if (style & WS_SYSMENU )
2668 /* switch to the system menu */
2669 hNewMenu = get_win_sys_menu( hNewWnd );
2673 else /* application returned a new menu to switch to */
2675 hNewMenu = next_menu.hmenuNext;
2676 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2678 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2680 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2682 if (style & WS_SYSMENU &&
2683 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2685 /* get the real system menu */
2686 hNewMenu = get_win_sys_menu(hNewWnd);
2688 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2690 /* FIXME: Not sure what to do here;
2691 * perhaps try to track hNewMenu as a popup? */
2693 TRACE(" -- got confused.\n");
2700 if( hNewMenu != pmt->hTopMenu )
2702 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2704 if( pmt->hCurrentMenu != pmt->hTopMenu )
2705 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2708 if( hNewWnd != pmt->hOwnerWnd )
2710 pmt->hOwnerWnd = hNewWnd;
2711 MENU_SetCapture( pmt->hOwnerWnd );
2714 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2715 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2722 /***********************************************************************
2725 * The idea is not to show the popup if the next input message is
2726 * going to hide it anyway.
2728 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2732 msg.hwnd = pmt->hOwnerWnd;
2734 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2735 pmt->trackFlags |= TF_SKIPREMOVE;
2740 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2741 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2743 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2744 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2745 if( msg.message == WM_KEYDOWN &&
2746 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2748 pmt->trackFlags |= TF_SUSPENDPOPUP;
2755 /* failures go through this */
2756 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2760 /***********************************************************************
2763 * Handle a VK_ESCAPE key event in a menu.
2765 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2767 BOOL bEndMenu = TRUE;
2769 if (pmt->hCurrentMenu != pmt->hTopMenu)
2771 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2773 if (menu->wFlags & MF_POPUP)
2775 HMENU hmenutmp, hmenuprev;
2777 hmenuprev = hmenutmp = pmt->hTopMenu;
2779 /* close topmost popup */
2780 while (hmenutmp != pmt->hCurrentMenu)
2782 hmenuprev = hmenutmp;
2783 hmenutmp = MENU_GetSubPopup( hmenuprev );
2786 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2787 pmt->hCurrentMenu = hmenuprev;
2795 /***********************************************************************
2798 * Handle a VK_LEFT key event in a menu.
2800 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2803 HMENU hmenutmp, hmenuprev;
2806 hmenuprev = hmenutmp = pmt->hTopMenu;
2807 menu = MENU_GetMenu( hmenutmp );
2809 /* Try to move 1 column left (if possible) */
2810 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2811 NO_SELECTED_ITEM ) {
2813 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2818 /* close topmost popup */
2819 while (hmenutmp != pmt->hCurrentMenu)
2821 hmenuprev = hmenutmp;
2822 hmenutmp = MENU_GetSubPopup( hmenuprev );
2825 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2826 pmt->hCurrentMenu = hmenuprev;
2828 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2830 /* move menu bar selection if no more popups are left */
2832 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2833 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2835 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2837 /* A sublevel menu was displayed - display the next one
2838 * unless there is another displacement coming up */
2840 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2841 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2842 pmt->hTopMenu, TRUE, wFlags);
2848 /***********************************************************************
2851 * Handle a VK_RIGHT key event in a menu.
2853 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2856 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2859 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2861 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2862 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2864 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2866 /* If already displaying a popup, try to display sub-popup */
2868 hmenutmp = pmt->hCurrentMenu;
2869 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2871 /* if subpopup was displayed then we are done */
2872 if (hmenutmp != pmt->hCurrentMenu) return;
2875 /* Check to see if there's another column */
2876 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2877 NO_SELECTED_ITEM ) {
2878 TRACE("Going to %d.\n", nextcol );
2879 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2884 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2886 if( pmt->hCurrentMenu != pmt->hTopMenu )
2888 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2889 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2890 } else hmenutmp = 0;
2892 /* try to move to the next item */
2893 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2894 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2896 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2897 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2898 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2899 pmt->hTopMenu, TRUE, wFlags);
2903 /***********************************************************************
2906 * Menu tracking code.
2908 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2909 HWND hwnd, const RECT *lprect )
2914 INT executedMenuId = -1;
2916 BOOL enterIdleSent = FALSE;
2919 mt.hCurrentMenu = hmenu;
2920 mt.hTopMenu = hmenu;
2921 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2925 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2926 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2929 if (!(menu = MENU_GetMenu( hmenu )))
2931 WARN("Invalid menu handle %p\n", hmenu);
2932 SetLastError(ERROR_INVALID_MENU_HANDLE);
2936 if (wFlags & TPM_BUTTONDOWN)
2938 /* Get the result in order to start the tracking or not */
2939 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2940 fEndMenu = !fRemove;
2943 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2945 MENU_SetCapture( mt.hOwnerWnd );
2949 menu = MENU_GetMenu( mt.hCurrentMenu );
2950 if (!menu) /* sometimes happens if I do a window manager close */
2953 /* we have to keep the message in the queue until it's
2954 * clear that menu loop is not over yet. */
2958 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2960 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2961 /* remove the message from the queue */
2962 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2968 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2969 enterIdleSent = TRUE;
2970 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2976 /* check if EndMenu() tried to cancel us, by posting this message */
2977 if(msg.message == WM_CANCELMODE)
2979 /* we are now out of the loop */
2982 /* remove the message from the queue */
2983 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2985 /* break out of internal loop, ala ESCAPE */
2989 TranslateMessage( &msg );
2992 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2993 enterIdleSent=FALSE;
2996 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2999 * Use the mouse coordinates in lParam instead of those in the MSG
3000 * struct to properly handle synthetic messages. They are already
3001 * in screen coordinates.
3003 mt.pt.x = (short)LOWORD(msg.lParam);
3004 mt.pt.y = (short)HIWORD(msg.lParam);
3006 /* Find a menu for this mouse event */
3007 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3011 /* no WM_NC... messages in captured state */
3013 case WM_RBUTTONDBLCLK:
3014 case WM_RBUTTONDOWN:
3015 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3017 case WM_LBUTTONDBLCLK:
3018 case WM_LBUTTONDOWN:
3019 /* If the message belongs to the menu, removes it from the queue */
3020 /* Else, end menu tracking */
3021 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3022 fEndMenu = !fRemove;
3026 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3029 /* Check if a menu was selected by the mouse */
3032 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3034 /* End the loop if executedMenuId is an item ID */
3035 /* or if the job was done (executedMenuId = 0). */
3036 fEndMenu = fRemove = (executedMenuId != -1);
3038 /* No menu was selected by the mouse */
3039 /* if the function was called by TrackPopupMenu, continue
3040 with the menu tracking. If not, stop it */
3042 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3047 /* the selected menu item must be changed every time */
3048 /* the mouse moves. */
3051 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3053 } /* switch(msg.message) - mouse */
3055 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3057 fRemove = TRUE; /* Keyboard messages are always removed */
3070 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3071 NO_SELECTED_ITEM, FALSE, 0 );
3072 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3073 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3077 case VK_DOWN: /* If on menu bar, pull-down the menu */
3079 menu = MENU_GetMenu( mt.hCurrentMenu );
3080 if (!(menu->wFlags & MF_POPUP))
3081 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3082 else /* otherwise try to move selection */
3083 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3084 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3088 MENU_KeyLeft( &mt, wFlags );
3092 MENU_KeyRight( &mt, wFlags );
3096 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3102 hi.cbSize = sizeof(HELPINFO);
3103 hi.iContextType = HELPINFO_MENUITEM;
3104 if (menu->FocusedItem == NO_SELECTED_ITEM)
3107 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3108 hi.hItemHandle = hmenu;
3109 hi.dwContextId = menu->dwContextHelpID;
3110 hi.MousePos = msg.pt;
3111 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3118 break; /* WM_KEYDOWN */
3125 if (msg.wParam == '\r' || msg.wParam == ' ')
3127 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3128 fEndMenu = (executedMenuId != -2);
3133 /* Hack to avoid control chars. */
3134 /* We will find a better way real soon... */
3135 if (msg.wParam < 32) break;
3137 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3138 LOWORD(msg.wParam), FALSE );
3139 if (pos == (UINT)-2) fEndMenu = TRUE;
3140 else if (pos == (UINT)-1) MessageBeep(0);
3143 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3145 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3146 fEndMenu = (executedMenuId != -2);
3150 } /* switch(msg.message) - kbd */
3154 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3155 DispatchMessageW( &msg );
3159 if (!fEndMenu) fRemove = TRUE;
3161 /* finally remove message from the queue */
3163 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3164 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3165 else mt.trackFlags &= ~TF_SKIPREMOVE;
3168 MENU_SetCapture(0); /* release the capture */
3170 /* If dropdown is still painted and the close box is clicked on
3171 then the menu will be destroyed as part of the DispatchMessage above.
3172 This will then invalidate the menu handle in mt.hTopMenu. We should
3173 check for this first. */
3174 if( IsMenu( mt.hTopMenu ) )
3176 menu = MENU_GetMenu( mt.hTopMenu );
3178 if( IsWindow( mt.hOwnerWnd ) )
3180 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3182 if (menu && (menu->wFlags & MF_POPUP))
3184 DestroyWindow( menu->hWnd );
3187 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3188 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3191 /* Reset the variable for hiding menu */
3192 if( menu ) menu->bTimeToHide = FALSE;
3195 /* The return value is only used by TrackPopupMenu */
3196 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3197 if (executedMenuId < 0) executedMenuId = 0;
3198 return executedMenuId;
3201 /***********************************************************************
3204 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3208 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3212 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3213 if (!(wFlags & TPM_NONOTIFY))
3214 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3216 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3218 if (!(wFlags & TPM_NONOTIFY))
3220 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3221 /* If an app changed/recreated menu bar entries in WM_INITMENU
3222 * menu sizes will be recalculated once the menu created/shown.
3226 /* This makes the menus of applications built with Delphi work.
3227 * It also enables menus to be displayed in more than one window,
3228 * but there are some bugs left that need to be fixed in this case.
3230 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3234 /***********************************************************************
3237 static BOOL MENU_ExitTracking(HWND hWnd)
3239 TRACE("hwnd=%p\n", hWnd);
3241 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3247 /***********************************************************************
3248 * MENU_TrackMouseMenuBar
3250 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3252 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3254 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3255 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3257 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3261 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3262 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3263 MENU_ExitTracking(hWnd);
3268 /***********************************************************************
3269 * MENU_TrackKbdMenuBar
3271 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3273 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3275 UINT uItem = NO_SELECTED_ITEM;
3277 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3279 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3281 /* find window that has a menu */
3283 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3284 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3286 /* check if we have to track a system menu */
3288 hTrackMenu = GetMenu( hwnd );
3289 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3291 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3292 hTrackMenu = get_win_sys_menu( hwnd );
3294 wParam |= HTSYSMENU; /* prevent item lookup */
3297 if (!IsMenu( hTrackMenu )) return;
3299 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3301 if( wChar && wChar != ' ' )
3303 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3304 if ( uItem >= (UINT)(-2) )
3306 if( uItem == (UINT)(-1) ) MessageBeep(0);
3307 /* schedule end of menu tracking */
3308 wFlags |= TF_ENDMENU;
3313 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3315 if (wParam & HTSYSMENU)
3317 /* prevent sysmenu activation for managed windows on Alt down/up */
3318 if (GetPropA( hwnd, "__wine_x11_managed" ))
3319 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3323 if( uItem == NO_SELECTED_ITEM )
3324 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3326 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3330 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3331 MENU_ExitTracking( hwnd );
3335 /**********************************************************************
3336 * TrackPopupMenu (USER32.@)
3338 * Like the win32 API, the function return the command ID only if the
3339 * flag TPM_RETURNCMD is on.
3342 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3343 INT nReserved, HWND hWnd, const RECT *lpRect )
3347 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3349 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3350 if (!(wFlags & TPM_NONOTIFY))
3351 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3353 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3354 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3355 MENU_ExitTracking(hWnd);
3360 /**********************************************************************
3361 * TrackPopupMenuEx (USER32.@)
3363 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3364 HWND hWnd, LPTPMPARAMS lpTpm )
3366 FIXME("not fully implemented\n" );
3367 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3368 lpTpm ? &lpTpm->rcExclude : NULL );
3371 /***********************************************************************
3374 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3376 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3378 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3384 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3385 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3389 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3390 return MA_NOACTIVATE;
3395 BeginPaint( hwnd, &ps );
3396 MENU_DrawPopupMenu( hwnd, ps.hdc,
3397 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3398 EndPaint( hwnd, &ps );
3405 /* zero out global pointer in case resident popup window was destroyed. */
3406 if (hwnd == top_popup) top_popup = 0;
3413 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3416 SetWindowLongPtrW( hwnd, 0, 0 );
3419 case MM_SETMENUHANDLE:
3420 SetWindowLongPtrW( hwnd, 0, wParam );
3423 case MM_GETMENUHANDLE:
3424 return GetWindowLongPtrW( hwnd, 0 );
3427 return DefWindowProcW( hwnd, message, wParam, lParam );
3433 /***********************************************************************
3434 * MENU_GetMenuBarHeight
3436 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3438 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3439 INT orgX, INT orgY )
3445 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3447 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3449 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3450 SelectObject( hdc, get_menu_font(FALSE));
3451 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3452 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3453 ReleaseDC( hwnd, hdc );
3454 return lppop->Height;
3458 /*******************************************************************
3459 * ChangeMenuA (USER32.@)
3461 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3462 UINT id, UINT flags )
3464 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3465 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3467 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3468 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3470 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3471 flags & MF_BYPOSITION ? pos : id,
3472 flags & ~MF_REMOVE );
3473 /* Default: MF_INSERT */
3474 return InsertMenuA( hMenu, pos, flags, id, data );
3478 /*******************************************************************
3479 * ChangeMenuW (USER32.@)
3481 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3482 UINT id, UINT flags )
3484 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3485 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3487 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3488 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3490 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3491 flags & MF_BYPOSITION ? pos : id,
3492 flags & ~MF_REMOVE );
3493 /* Default: MF_INSERT */
3494 return InsertMenuW( hMenu, pos, flags, id, data );
3498 /*******************************************************************
3499 * CheckMenuItem (USER32.@)
3501 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3506 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3507 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3508 ret = item->fState & MF_CHECKED;
3509 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3510 else item->fState &= ~MF_CHECKED;
3515 /**********************************************************************
3516 * EnableMenuItem (USER32.@)
3518 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3524 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3526 /* Get the Popupmenu to access the owner menu */
3527 if (!(menu = MENU_GetMenu(hMenu)))
3530 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3533 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3534 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3536 /* If the close item in the system menu change update the close button */
3537 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3539 if (menu->hSysMenuOwner != 0)
3542 POPUPMENU* parentMenu;
3544 /* Get the parent menu to access*/
3545 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3548 /* Refresh the frame to reflect the change */
3549 GetWindowRect(parentMenu->hWnd, &rc);
3550 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3552 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3560 /*******************************************************************
3561 * GetMenuStringA (USER32.@)
3563 INT WINAPI GetMenuStringA(
3564 HMENU hMenu, /* [in] menuhandle */
3565 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3566 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3567 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3568 UINT wFlags /* [in] MF_ flags */
3572 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3573 if (str && nMaxSiz) str[0] = '\0';
3574 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3575 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3578 if (!item->text) return 0;
3579 if (!str || !nMaxSiz) return strlenW(item->text);
3580 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3582 TRACE("returning '%s'\n", str );
3587 /*******************************************************************
3588 * GetMenuStringW (USER32.@)
3590 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3591 LPWSTR str, INT nMaxSiz, UINT wFlags )
3595 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3596 if (str && nMaxSiz) str[0] = '\0';
3597 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3598 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3601 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3602 if( !(item->text)) {
3606 lstrcpynW( str, item->text, nMaxSiz );
3607 return strlenW(str);
3611 /**********************************************************************
3612 * HiliteMenuItem (USER32.@)
3614 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3618 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3619 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3620 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3621 if (menu->FocusedItem == wItemID) return TRUE;
3622 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3623 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3628 /**********************************************************************
3629 * GetMenuState (USER32.@)
3631 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3634 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3635 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3636 debug_print_menuitem (" item: ", item, "");
3637 if (item->fType & MF_POPUP)
3639 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3640 if (!menu) return -1;
3641 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3645 /* We used to (from way back then) mask the result to 0xff. */
3646 /* I don't know why and it seems wrong as the documented */
3647 /* return flag MF_SEPARATOR is outside that mask. */
3648 return (item->fType | item->fState);
3653 /**********************************************************************
3654 * GetMenuItemCount (USER32.@)
3656 INT WINAPI GetMenuItemCount( HMENU hMenu )
3658 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3659 if (!menu) return -1;
3660 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3661 return menu->nItems;
3665 /**********************************************************************
3666 * GetMenuItemID (USER32.@)
3668 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3672 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3673 if (lpmi->fType & MF_POPUP) return -1;
3679 /*******************************************************************
3680 * InsertMenuW (USER32.@)
3682 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3683 UINT_PTR id, LPCWSTR str )
3687 if (IS_STRING_ITEM(flags) && str)
3688 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3689 hMenu, pos, flags, id, debugstr_w(str) );
3690 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3691 hMenu, pos, flags, id, str );
3693 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3695 if (!(MENU_SetItemData( item, flags, id, str )))
3697 RemoveMenu( hMenu, pos, flags );
3701 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3702 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3704 item->hCheckBit = item->hUnCheckBit = 0;
3709 /*******************************************************************
3710 * InsertMenuA (USER32.@)
3712 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3713 UINT_PTR id, LPCSTR str )
3717 if (IS_STRING_ITEM(flags) && str)
3719 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3720 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3723 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3724 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3725 HeapFree( GetProcessHeap(), 0, newstr );
3729 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3733 /*******************************************************************
3734 * AppendMenuA (USER32.@)
3736 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3737 UINT_PTR id, LPCSTR data )
3739 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3743 /*******************************************************************
3744 * AppendMenuW (USER32.@)
3746 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3747 UINT_PTR id, LPCWSTR data )
3749 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3753 /**********************************************************************
3754 * RemoveMenu (USER32.@)
3756 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3761 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3762 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3763 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3767 MENU_FreeItemData( item );
3769 if (--menu->nItems == 0)
3771 HeapFree( GetProcessHeap(), 0, menu->items );
3776 while(nPos < menu->nItems)
3782 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3783 menu->nItems * sizeof(MENUITEM) );
3789 /**********************************************************************
3790 * DeleteMenu (USER32.@)
3792 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3794 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3795 if (!item) return FALSE;
3796 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3797 /* nPos is now the position of the item */
3798 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3803 /*******************************************************************
3804 * ModifyMenuW (USER32.@)
3806 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3807 UINT_PTR id, LPCWSTR str )
3811 if (IS_STRING_ITEM(flags))
3812 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3814 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3816 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3817 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3818 return MENU_SetItemData( item, flags, id, str );
3822 /*******************************************************************
3823 * ModifyMenuA (USER32.@)
3825 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3826 UINT_PTR id, LPCSTR str )
3830 if (IS_STRING_ITEM(flags) && str)
3832 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3833 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3836 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3837 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3838 HeapFree( GetProcessHeap(), 0, newstr );
3842 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3846 /**********************************************************************
3847 * CreatePopupMenu (USER32.@)
3849 HMENU WINAPI CreatePopupMenu(void)
3854 if (!(hmenu = CreateMenu())) return 0;
3855 menu = MENU_GetMenu( hmenu );
3856 menu->wFlags |= MF_POPUP;
3857 menu->bTimeToHide = FALSE;
3862 /**********************************************************************
3863 * GetMenuCheckMarkDimensions (USER.417)
3864 * GetMenuCheckMarkDimensions (USER32.@)
3866 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3868 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3872 /**********************************************************************
3873 * SetMenuItemBitmaps (USER32.@)
3875 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3876 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3879 TRACE("(%p, %04x, %04x, %p, %p)\n",
3880 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3881 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3883 if (!hNewCheck && !hNewUnCheck)
3885 item->fState &= ~MF_USECHECKBITMAPS;
3887 else /* Install new bitmaps */
3889 item->hCheckBit = hNewCheck;
3890 item->hUnCheckBit = hNewUnCheck;
3891 item->fState |= MF_USECHECKBITMAPS;
3897 /**********************************************************************
3898 * CreateMenu (USER32.@)
3900 HMENU WINAPI CreateMenu(void)
3904 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3905 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3907 ZeroMemory(menu, sizeof(POPUPMENU));
3908 menu->wMagic = MENU_MAGIC;
3909 menu->FocusedItem = NO_SELECTED_ITEM;
3910 menu->bTimeToHide = FALSE;
3912 TRACE("return %p\n", hMenu );
3918 /**********************************************************************
3919 * DestroyMenu (USER32.@)
3921 BOOL WINAPI DestroyMenu( HMENU hMenu )
3923 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3925 TRACE("(%p)\n", hMenu);
3928 if (!lppop) return FALSE;
3930 lppop->wMagic = 0; /* Mark it as destroyed */
3932 /* DestroyMenu should not destroy system menu popup owner */
3933 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3935 DestroyWindow( lppop->hWnd );
3939 if (lppop->items) /* recursively destroy submenus */
3942 MENUITEM *item = lppop->items;
3943 for (i = lppop->nItems; i > 0; i--, item++)
3945 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3946 MENU_FreeItemData( item );
3948 HeapFree( GetProcessHeap(), 0, lppop->items );
3950 USER_HEAP_FREE( hMenu );
3955 /**********************************************************************
3956 * GetSystemMenu (USER32.@)
3958 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3960 WND *wndPtr = WIN_GetPtr( hWnd );
3963 if (wndPtr == WND_DESKTOP) return 0;
3964 if (wndPtr == WND_OTHER_PROCESS)
3966 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3970 if (wndPtr->hSysMenu && bRevert)
3972 DestroyMenu(wndPtr->hSysMenu);
3973 wndPtr->hSysMenu = 0;
3976 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3977 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3979 if( wndPtr->hSysMenu )
3982 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3984 /* Store the dummy sysmenu handle to facilitate the refresh */
3985 /* of the close button if the SC_CLOSE item change */
3986 menu = MENU_GetMenu(retvalue);
3988 menu->hSysMenuOwner = wndPtr->hSysMenu;
3990 WIN_ReleasePtr( wndPtr );
3992 return bRevert ? 0 : retvalue;
3996 /*******************************************************************
3997 * SetSystemMenu (USER32.@)
3999 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4001 WND *wndPtr = WIN_GetPtr( hwnd );
4003 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4005 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4006 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4007 WIN_ReleasePtr( wndPtr );
4014 /**********************************************************************
4015 * GetMenu (USER32.@)
4017 HMENU WINAPI GetMenu( HWND hWnd )
4019 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4020 TRACE("for %p returning %p\n", hWnd, retvalue);
4024 /**********************************************************************
4025 * GetMenuBarInfo (USER32.@)
4027 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4029 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4033 /**********************************************************************
4036 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4037 * SetWindowPos call that would result if SetMenu were called directly.
4039 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4041 TRACE("(%p, %p);\n", hWnd, hMenu);
4043 if (hMenu && !IsMenu(hMenu))
4045 WARN("hMenu %p is not a menu handle\n", hMenu);
4048 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4051 hWnd = WIN_GetFullHandle( hWnd );
4052 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4058 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4060 lpmenu->hWnd = hWnd;
4061 lpmenu->Height = 0; /* Make sure we recalculate the size */
4063 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4068 /**********************************************************************
4069 * SetMenu (USER32.@)
4071 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4073 if(!MENU_SetMenu(hWnd, hMenu))
4076 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4077 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4082 /**********************************************************************
4083 * GetSubMenu (USER32.@)
4085 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4089 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4090 if (!(lpmi->fType & MF_POPUP)) return 0;
4091 return lpmi->hSubMenu;
4095 /**********************************************************************
4096 * DrawMenuBar (USER32.@)
4098 BOOL WINAPI DrawMenuBar( HWND hWnd )
4101 HMENU hMenu = GetMenu(hWnd);
4103 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4105 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4107 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4108 lppop->hwndOwner = hWnd;
4109 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4110 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4114 /***********************************************************************
4115 * DrawMenuBarTemp (USER32.@)
4119 * called by W98SE desk.cpl Control Panel Applet
4121 * Not 100% sure about the param names, but close.
4123 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4128 BOOL flat_menu = FALSE;
4130 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4133 hMenu = GetMenu(hwnd);
4136 hFont = get_menu_font(FALSE);
4138 lppop = MENU_GetMenu( hMenu );
4139 if (lppop == NULL || lprect == NULL)
4141 retvalue = GetSystemMetrics(SM_CYMENU);
4145 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4147 hfontOld = SelectObject( hDC, hFont);
4149 if (lppop->Height == 0)
4150 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4152 lprect->bottom = lprect->top + lppop->Height;
4154 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4156 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4157 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4158 LineTo( hDC, lprect->right, lprect->bottom );
4160 if (lppop->nItems == 0)
4162 retvalue = GetSystemMetrics(SM_CYMENU);
4166 for (i = 0; i < lppop->nItems; i++)
4168 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4169 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4171 retvalue = lppop->Height;
4174 if (hfontOld) SelectObject (hDC, hfontOld);
4178 /***********************************************************************
4179 * EndMenu (USER.187)
4180 * EndMenu (USER32.@)
4182 void WINAPI EndMenu(void)
4184 /* if we are in the menu code, and it is active */
4185 if (!fEndMenu && top_popup)
4187 /* terminate the menu handling code */
4190 /* needs to be posted to wakeup the internal menu handler */
4191 /* which will now terminate the menu, in the event that */
4192 /* the main window was minimized, or lost focus, so we */
4193 /* don't end up with an orphaned menu */
4194 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4199 /***********************************************************************
4200 * LookupMenuHandle (USER.217)
4202 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4204 HMENU hmenu32 = HMENU_32(hmenu);
4206 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4207 else return HMENU_16(hmenu32);
4211 /**********************************************************************
4212 * LoadMenu (USER.150)
4214 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4220 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4221 if (!name) return 0;
4223 instance = GetExePtr( instance );
4224 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4225 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4226 hMenu = LoadMenuIndirect16(LockResource16(handle));
4227 FreeResource16( handle );
4232 /*****************************************************************
4233 * LoadMenuA (USER32.@)
4235 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4237 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4238 if (!hrsrc) return 0;
4239 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4243 /*****************************************************************
4244 * LoadMenuW (USER32.@)
4246 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4248 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4249 if (!hrsrc) return 0;
4250 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4254 /**********************************************************************
4255 * LoadMenuIndirect (USER.220)
4257 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4260 WORD version, offset;
4261 LPCSTR p = (LPCSTR)template;
4263 TRACE("(%p)\n", template );
4264 version = GET_WORD(p);
4268 WARN("version must be 0 for Win16\n" );
4271 offset = GET_WORD(p);
4272 p += sizeof(WORD) + offset;
4273 if (!(hMenu = CreateMenu())) return 0;
4274 if (!MENU_ParseResource( p, hMenu, FALSE ))
4276 DestroyMenu( hMenu );
4279 return HMENU_16(hMenu);
4283 /**********************************************************************
4284 * LoadMenuIndirectW (USER32.@)
4286 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4289 WORD version, offset;
4290 LPCSTR p = (LPCSTR)template;
4292 version = GET_WORD(p);
4294 TRACE("%p, ver %d\n", template, version );
4297 case 0: /* standard format is version of 0 */
4298 offset = GET_WORD(p);
4299 p += sizeof(WORD) + offset;
4300 if (!(hMenu = CreateMenu())) return 0;
4301 if (!MENU_ParseResource( p, hMenu, TRUE ))
4303 DestroyMenu( hMenu );
4307 case 1: /* extended format is version of 1 */
4308 offset = GET_WORD(p);
4309 p += sizeof(WORD) + offset;
4310 if (!(hMenu = CreateMenu())) return 0;
4311 if (!MENUEX_ParseResource( p, hMenu))
4313 DestroyMenu( hMenu );
4318 ERR("version %d not supported.\n", version);
4324 /**********************************************************************
4325 * LoadMenuIndirectA (USER32.@)
4327 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4329 return LoadMenuIndirectW( template );
4333 /**********************************************************************
4336 BOOL WINAPI IsMenu(HMENU hmenu)
4338 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4342 SetLastError(ERROR_INVALID_MENU_HANDLE);
4348 /**********************************************************************
4349 * GetMenuItemInfo_common
4352 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4353 LPMENUITEMINFOW lpmii, BOOL unicode)
4355 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4357 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4362 if( lpmii->fMask & MIIM_TYPE) {
4363 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4364 WARN("invalid combination of fMask bits used\n");
4365 /* this does not happen on Win9x/ME */
4366 SetLastError( ERROR_INVALID_PARAMETER);
4369 lpmii->fType = menu->fType & ~MF_POPUP;
4370 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4371 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4372 if( lpmii->fType & MFT_BITMAP) {
4373 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4375 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4376 /* this does not happen on Win9x/ME */
4377 lpmii->dwTypeData = 0;
4382 /* copy the text string */
4383 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4385 if(lpmii->dwTypeData && lpmii->cch) {
4388 *((WCHAR *)lpmii->dwTypeData) = 0;
4390 *((CHAR *)lpmii->dwTypeData) = 0;
4396 len = strlenW(menu->text);
4397 if(lpmii->dwTypeData && lpmii->cch)
4398 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4402 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4403 0, NULL, NULL ) - 1;
4404 if(lpmii->dwTypeData && lpmii->cch)
4405 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4406 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4407 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4409 /* if we've copied a substring we return its length */
4410 if(lpmii->dwTypeData && lpmii->cch)
4411 if (lpmii->cch <= len + 1)
4416 /* return length of string */
4417 /* not on Win9x/ME if fType & MFT_BITMAP */
4423 if (lpmii->fMask & MIIM_FTYPE)
4424 lpmii->fType = menu->fType & ~MF_POPUP;
4426 if (lpmii->fMask & MIIM_BITMAP)
4427 lpmii->hbmpItem = menu->hbmpItem;
4429 if (lpmii->fMask & MIIM_STATE)
4430 lpmii->fState = menu->fState;
4432 if (lpmii->fMask & MIIM_ID)
4433 lpmii->wID = menu->wID;
4435 if (lpmii->fMask & MIIM_SUBMENU)
4436 lpmii->hSubMenu = menu->hSubMenu;
4438 /* hSubMenu is always cleared
4439 * (not on Win9x/ME ) */
4440 lpmii->hSubMenu = 0;
4443 if (lpmii->fMask & MIIM_CHECKMARKS) {
4444 lpmii->hbmpChecked = menu->hCheckBit;
4445 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4447 if (lpmii->fMask & MIIM_DATA)
4448 lpmii->dwItemData = menu->dwItemData;
4453 /**********************************************************************
4454 * GetMenuItemInfoA (USER32.@)
4456 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4457 LPMENUITEMINFOA lpmii)
4461 if( lpmii->cbSize != sizeof( mii) &&
4462 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4463 SetLastError( ERROR_INVALID_PARAMETER);
4466 memcpy( &mii, lpmii, lpmii->cbSize);
4467 mii.cbSize = sizeof( mii);
4468 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4469 (LPMENUITEMINFOW)&mii, FALSE);
4470 mii.cbSize = lpmii->cbSize;
4471 memcpy( lpmii, &mii, mii.cbSize);
4475 /**********************************************************************
4476 * GetMenuItemInfoW (USER32.@)
4478 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4479 LPMENUITEMINFOW lpmii)
4483 if( lpmii->cbSize != sizeof( mii) &&
4484 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4485 SetLastError( ERROR_INVALID_PARAMETER);
4488 memcpy( &mii, lpmii, lpmii->cbSize);
4489 mii.cbSize = sizeof( mii);
4490 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4491 mii.cbSize = lpmii->cbSize;
4492 memcpy( lpmii, &mii, mii.cbSize);
4497 /* set a menu item text from a ASCII or Unicode string */
4498 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4504 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4505 strcpyW( menu->text, text );
4509 LPCSTR str = (LPCSTR)text;
4510 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4511 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4512 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4517 /**********************************************************************
4518 * SetMenuItemInfo_common
4521 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4522 const MENUITEMINFOW *lpmii,
4525 if (!menu) return FALSE;
4527 debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4529 if (lpmii->fMask & MIIM_TYPE ) {
4530 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4531 WARN("invalid combination of fMask bits used\n");
4532 /* this does not happen on Win9x/ME */
4533 SetLastError( ERROR_INVALID_PARAMETER);
4536 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4537 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4538 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4540 if (IS_STRING_ITEM(menu->fType)) {
4541 HeapFree(GetProcessHeap(), 0, menu->text);
4542 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4543 } else if( (menu->fType) & MFT_BITMAP)
4544 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4547 if (lpmii->fMask & MIIM_FTYPE ) {
4548 if(( lpmii->fType & MFT_BITMAP)) {
4549 SetLastError( ERROR_INVALID_PARAMETER);
4552 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4553 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4555 if (lpmii->fMask & MIIM_STRING ) {
4556 /* free the string when used */
4557 HeapFree(GetProcessHeap(), 0, menu->text);
4558 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4561 if (lpmii->fMask & MIIM_STATE)
4563 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4564 menu->fState = lpmii->fState;
4567 if (lpmii->fMask & MIIM_ID)
4568 menu->wID = lpmii->wID;
4570 if (lpmii->fMask & MIIM_SUBMENU) {
4571 menu->hSubMenu = lpmii->hSubMenu;
4572 if (menu->hSubMenu) {
4573 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4575 subMenu->wFlags |= MF_POPUP;
4576 menu->fType |= MF_POPUP;
4579 SetLastError( ERROR_INVALID_PARAMETER);
4584 menu->fType &= ~MF_POPUP;
4587 if (lpmii->fMask & MIIM_CHECKMARKS)
4589 if (lpmii->fType & MFT_RADIOCHECK)
4590 menu->fType |= MFT_RADIOCHECK;
4592 menu->hCheckBit = lpmii->hbmpChecked;
4593 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4595 if (lpmii->fMask & MIIM_DATA)
4596 menu->dwItemData = lpmii->dwItemData;
4598 if (lpmii->fMask & MIIM_BITMAP)
4599 menu->hbmpItem = lpmii->hbmpItem;
4601 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4602 menu->fType |= MFT_SEPARATOR;
4604 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4608 /**********************************************************************
4609 * SetMenuItemInfoA (USER32.@)
4611 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4612 const MENUITEMINFOA *lpmii)
4615 if( lpmii->cbSize != sizeof( mii) &&
4616 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4617 SetLastError( ERROR_INVALID_PARAMETER);
4620 memcpy( &mii, lpmii, lpmii->cbSize);
4621 if( lpmii->cbSize != sizeof( mii)) {
4622 mii.cbSize = sizeof( mii);
4623 mii.hbmpItem = NULL;
4625 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4626 (const MENUITEMINFOW *)&mii, FALSE);
4629 /**********************************************************************
4630 * SetMenuItemInfoW (USER32.@)
4632 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4633 const MENUITEMINFOW *lpmii)
4636 if( lpmii->cbSize != sizeof( mii) &&
4637 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4638 SetLastError( ERROR_INVALID_PARAMETER);
4641 memcpy( &mii, lpmii, lpmii->cbSize);
4642 if( lpmii->cbSize != sizeof( mii)) {
4643 mii.cbSize = sizeof( mii);
4644 mii.hbmpItem = NULL;
4646 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4647 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4650 /**********************************************************************
4651 * SetMenuDefaultItem (USER32.@)
4654 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4660 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4662 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4664 /* reset all default-item flags */
4666 for (i = 0; i < menu->nItems; i++, item++)
4668 item->fState &= ~MFS_DEFAULT;
4671 /* no default item */
4680 if ( uItem >= menu->nItems ) return FALSE;
4681 item[uItem].fState |= MFS_DEFAULT;
4686 for (i = 0; i < menu->nItems; i++, item++)
4688 if (item->wID == uItem)
4690 item->fState |= MFS_DEFAULT;
4699 /**********************************************************************
4700 * GetMenuDefaultItem (USER32.@)
4702 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4708 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4710 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4712 /* find default item */
4716 if (! item) return -1;
4718 while ( !( item->fState & MFS_DEFAULT ) )
4721 if (i >= menu->nItems ) return -1;
4724 /* default: don't return disabled items */
4725 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4727 /* search rekursiv when needed */
4728 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4731 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4732 if ( -1 != ret ) return ret;
4734 /* when item not found in submenu, return the popup item */
4736 return ( bypos ) ? i : item->wID;
4741 /**********************************************************************
4742 * InsertMenuItemA (USER32.@)
4744 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4745 const MENUITEMINFOA *lpmii)
4747 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4749 if( lpmii->cbSize != sizeof( mii) &&
4750 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4751 SetLastError( ERROR_INVALID_PARAMETER);
4754 memcpy( &mii, lpmii, lpmii->cbSize);
4755 if( lpmii->cbSize != sizeof( mii)) {
4756 mii.cbSize = sizeof( mii);
4757 mii.hbmpItem = NULL;
4759 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4763 /**********************************************************************
4764 * InsertMenuItemW (USER32.@)
4766 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4767 const MENUITEMINFOW *lpmii)
4769 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4771 if( lpmii->cbSize != sizeof( mii) &&
4772 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4773 SetLastError( ERROR_INVALID_PARAMETER);
4776 memcpy( &mii, lpmii, lpmii->cbSize);
4777 if( lpmii->cbSize != sizeof( mii)) {
4778 mii.cbSize = sizeof( mii);
4779 mii.hbmpItem = NULL;
4781 return SetMenuItemInfo_common(item, &mii, TRUE);
4784 /**********************************************************************
4785 * CheckMenuRadioItem (USER32.@)
4788 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4789 UINT first, UINT last, UINT check,
4792 MENUITEM *mifirst, *milast, *micheck;
4793 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4795 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4797 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4798 milast = MENU_FindItem (&mlast, &last, bypos);
4799 micheck = MENU_FindItem (&mcheck, &check, bypos);
4801 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4802 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4803 micheck > milast || micheck < mifirst)
4806 while (mifirst <= milast)
4808 if (mifirst == micheck)
4810 mifirst->fType |= MFT_RADIOCHECK;
4811 mifirst->fState |= MFS_CHECKED;
4813 mifirst->fType &= ~MFT_RADIOCHECK;
4814 mifirst->fState &= ~MFS_CHECKED;
4823 /**********************************************************************
4824 * GetMenuItemRect (USER32.@)
4826 * ATTENTION: Here, the returned values in rect are the screen
4827 * coordinates of the item just like if the menu was
4828 * always on the upper left side of the application.
4831 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4834 POPUPMENU *itemMenu;
4838 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4840 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4841 referenceHwnd = hwnd;
4845 itemMenu = MENU_GetMenu(hMenu);
4846 if (itemMenu == NULL)
4849 if(itemMenu->hWnd == 0)
4851 referenceHwnd = itemMenu->hWnd;
4854 if ((rect == NULL) || (item == NULL))
4859 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4865 /**********************************************************************
4866 * SetMenuInfo (USER32.@)
4869 * MIM_APPLYTOSUBMENUS
4870 * actually use the items to draw the menu
4872 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4876 TRACE("(%p %p)\n", hMenu, lpmi);
4878 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4881 if (lpmi->fMask & MIM_BACKGROUND)
4882 menu->hbrBack = lpmi->hbrBack;
4884 if (lpmi->fMask & MIM_HELPID)
4885 menu->dwContextHelpID = lpmi->dwContextHelpID;
4887 if (lpmi->fMask & MIM_MAXHEIGHT)
4888 menu->cyMax = lpmi->cyMax;
4890 if (lpmi->fMask & MIM_MENUDATA)
4891 menu->dwMenuData = lpmi->dwMenuData;
4893 if (lpmi->fMask & MIM_STYLE)
4895 menu->dwStyle = lpmi->dwStyle;
4896 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4897 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4898 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4899 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4907 /**********************************************************************
4908 * GetMenuInfo (USER32.@)
4914 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4917 TRACE("(%p %p)\n", hMenu, lpmi);
4919 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4922 if (lpmi->fMask & MIM_BACKGROUND)
4923 lpmi->hbrBack = menu->hbrBack;
4925 if (lpmi->fMask & MIM_HELPID)
4926 lpmi->dwContextHelpID = menu->dwContextHelpID;
4928 if (lpmi->fMask & MIM_MAXHEIGHT)
4929 lpmi->cyMax = menu->cyMax;
4931 if (lpmi->fMask & MIM_MENUDATA)
4932 lpmi->dwMenuData = menu->dwMenuData;
4934 if (lpmi->fMask & MIM_STYLE)
4935 lpmi->dwStyle = menu->dwStyle;
4943 /**********************************************************************
4944 * SetMenuContextHelpId (USER32.@)
4946 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4950 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4952 if ((menu = MENU_GetMenu(hMenu)))
4954 menu->dwContextHelpID = dwContextHelpID;
4961 /**********************************************************************
4962 * GetMenuContextHelpId (USER32.@)
4964 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4968 TRACE("(%p)\n", hMenu);
4970 if ((menu = MENU_GetMenu(hMenu)))
4972 return menu->dwContextHelpID;
4977 /**********************************************************************
4978 * MenuItemFromPoint (USER32.@)
4980 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4982 POPUPMENU *menu = MENU_GetMenu(hMenu);
4985 /*FIXME: Do we have to handle hWnd here? */
4986 if (!menu) return -1;
4987 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4992 /**********************************************************************
4993 * translate_accelerator
4995 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4996 BYTE fVirt, WORD key, WORD cmd )
5001 if (wParam != key) return FALSE;
5003 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5004 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5005 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5007 if (message == WM_CHAR || message == WM_SYSCHAR)
5009 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5011 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5017 if(fVirt & FVIRTKEY)
5019 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5020 wParam, 0xff & HIWORD(lParam));
5022 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5023 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5027 if (!(lParam & 0x01000000)) /* no special_key */
5029 if ((fVirt & FALT) && (lParam & 0x20000000))
5030 { /* ^^ ALT pressed */
5031 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5040 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5044 HMENU hMenu, hSubMenu, hSysMenu;
5045 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5047 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5048 hSysMenu = get_win_sys_menu( hWnd );
5050 /* find menu item and ask application to initialize it */
5051 /* 1. in the system menu */
5052 hSubMenu = hSysMenu;
5054 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5058 if (!IsWindowEnabled(hWnd))
5062 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5063 if(hSubMenu != hSysMenu)
5065 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5066 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5067 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5069 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5072 else /* 2. in the window's menu */
5076 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5080 if (!IsWindowEnabled(hWnd))
5084 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5085 if(hSubMenu != hMenu)
5087 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5088 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5089 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5091 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5098 if (uSysStat != (UINT)-1)
5100 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5107 if (uStat != (UINT)-1)
5113 if (uStat & (MF_DISABLED|MF_GRAYED))
5125 if( mesg==WM_COMMAND )
5127 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5128 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5130 else if( mesg==WM_SYSCOMMAND )
5132 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5133 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5137 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5138 * #0: unknown (please report!)
5139 * #1: for WM_KEYUP,WM_SYSKEYUP
5140 * #2: mouse is captured
5141 * #3: window is disabled
5142 * #4: it's a disabled system menu option
5143 * #5: it's a menu option, but window is iconic
5144 * #6: it's a menu option, but disabled
5146 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5148 ERR_(accel)(" unknown reason - please report!\n");
5153 /**********************************************************************
5154 * TranslateAcceleratorA (USER32.@)
5155 * TranslateAccelerator (USER32.@)
5157 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5160 LPACCEL16 lpAccelTbl;
5164 if (!hWnd || !msg) return 0;
5166 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5168 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5172 wParam = msg->wParam;
5174 switch (msg->message)
5183 char ch = LOWORD(wParam);
5185 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5186 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5194 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5195 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5199 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5200 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5202 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5207 /**********************************************************************
5208 * TranslateAcceleratorW (USER32.@)
5210 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5213 LPACCEL16 lpAccelTbl;
5216 if (!hWnd || !msg) return 0;
5218 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5220 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5224 switch (msg->message)
5236 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5237 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5241 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5242 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5244 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);