4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType; /* Item type. */
76 UINT fState; /* Item state. */
77 UINT_PTR wID; /* Item id. */
78 HMENU hSubMenu; /* Pop-up menu. */
79 HBITMAP hCheckBit; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
81 LPWSTR text; /* Item text. */
82 ULONG_PTR dwItemData; /* Application defined. */
83 LPWSTR dwTypeData; /* depends on fMask */
84 HBITMAP hbmpItem; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect; /* Item area (relative to menu window) */
87 UINT xTab; /* X position of text after Tab */
88 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
92 /* Popup menu structure */
94 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic; /* Magic number */
96 WORD Width; /* Width of the whole menu */
97 WORD Height; /* Height of the whole menu */
98 UINT nItems; /* Number of items in the menu */
99 HWND hWnd; /* Window containing the menu */
100 MENUITEM *items; /* Array of menu items */
101 UINT FocusedItem; /* Currently focused item */
102 HWND hwndOwner; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling; /* Scroll arrows are active */
105 UINT nScrollPos; /* Current scroll position */
106 UINT nTotalHeight; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle; /* Extended menu style */
109 UINT cyMax; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack; /* brush for menu background */
111 DWORD dwContextHelpID;
112 DWORD dwMenuData; /* application defined value */
113 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize; /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
126 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu; /* initial menu */
128 HWND hOwnerWnd; /* where notifications are sent */
132 #define MENU_MAGIC 0x554d /* 'MU' */
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 (LPCSTR)POPUPMENU_CLASS_ATOM, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
222 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix);
228 UINT flags = mp->fType;
229 TRACE( "{ ID=0x%lx", mp->wID);
231 TRACE( ", Sub=%p", mp->hSubMenu);
235 MENUFLAG( MFT_SEPARATOR, "sep");
236 MENUFLAG( MFT_OWNERDRAW, "own");
237 MENUFLAG( MFT_BITMAP, "bit");
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 TRACE( "+0x%x", flags);
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
260 TRACE( "+0x%x", flags);
263 TRACE( ", Chk=%p", mp->hCheckBit);
265 TRACE( ", Unc=%p", mp->hUnCheckBit);
267 TRACE( ", Text=%s", debugstr_w(mp->text));
269 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if( IS_MAGIC_BITMAP(mp->hbmpItem))
273 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
275 TRACE( ", hbitmap=%p", mp->hbmpItem);
280 TRACE(" %s\n", postfix);
287 /***********************************************************************
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
294 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295 if (!menu || menu->wMagic != MENU_MAGIC)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
303 /***********************************************************************
306 * Get the system menu of a window
308 static HMENU get_win_sys_menu( HWND hwnd )
311 WND *win = WIN_GetPtr( hwnd );
312 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
315 WIN_ReleasePtr( win );
320 /***********************************************************************
323 static HFONT get_menu_font( BOOL bold )
325 static HFONT hMenuFont, hMenuFontBold;
327 HFONT ret = bold ? hMenuFontBold : hMenuFont;
331 NONCLIENTMETRICSW ncm;
334 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
339 ncm.lfMenuFont.lfWeight += 300;
340 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
342 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
347 /* another thread beat us to it */
355 /***********************************************************************
358 static HBITMAP get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap;
362 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
410 /***********************************************************************
413 * Return the default system menu.
415 static HMENU MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
421 POPUPMENU* menu = MENU_GetMenu(hMenu);
422 menu->wFlags |= MF_SYSMENU | MF_POPUP;
423 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu );
434 /**********************************************************************
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448 if ((hMenu = CreateMenu()))
450 POPUPMENU *menu = MENU_GetMenu(hMenu);
451 menu->wFlags = MF_SYSMENU;
452 menu->hWnd = WIN_GetFullHandle( hWnd );
453 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
456 hPopupMenu = MENU_CopySysPopup();
460 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
463 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464 (UINT_PTR)hPopupMenu, NULL );
466 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467 menu->items[0].fState = 0;
468 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
473 DestroyMenu( hMenu );
475 ERR("failed to load system menu!\n");
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
489 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = ((style & WS_MAXIMIZE) != 0);
492 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499 gray = (clsStyle & CS_NOCLOSE) != 0;
501 /* The menu item must keep its state if it's disabled */
503 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
512 *****************************************************************************/
514 static UINT MENU_GetStartOfNextColumn(
517 POPUPMENU *menu = MENU_GetMenu(hMenu);
521 return NO_SELECTED_ITEM;
523 i = menu->FocusedItem + 1;
524 if( i == NO_SELECTED_ITEM )
527 for( ; i < menu->nItems; ++i ) {
528 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
532 return NO_SELECTED_ITEM;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
541 *****************************************************************************/
543 static UINT MENU_GetStartOfPrevColumn(
546 POPUPMENU *menu = MENU_GetMenu(hMenu);
550 return NO_SELECTED_ITEM;
552 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553 return NO_SELECTED_ITEM;
555 /* Find the start of the column */
557 for(i = menu->FocusedItem; i != 0 &&
558 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
562 return NO_SELECTED_ITEM;
564 for(--i; i != 0; --i) {
565 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
569 TRACE("ret %d.\n", i );
576 /***********************************************************************
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
585 MENUITEM *fallback = NULL;
586 UINT fallback_pos = 0;
589 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590 if (wFlags & MF_BYPOSITION)
592 if (*nPos >= menu->nItems) return NULL;
593 return &menu->items[*nPos];
597 MENUITEM *item = menu->items;
598 for (i = 0; i < menu->nItems; i++, item++)
600 if (item->fType & MF_POPUP)
602 HMENU hsubmenu = item->hSubMenu;
603 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
609 else if (item->wID == *nPos)
611 /* fallback to this item if nothing else found */
616 else if (item->wID == *nPos)
625 *nPos = fallback_pos;
630 /***********************************************************************
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
642 if (((*hmenu)==(HMENU)0xffff) ||
643 (!(menu = MENU_GetMenu(*hmenu))))
644 return NO_SELECTED_ITEM;
646 for (i = 0; i < menu->nItems; i++, item++) {
647 if(!(item->fType & MF_POPUP)) continue;
648 if (item->hSubMenu == hSubTarget) {
652 HMENU hsubmenu = item->hSubMenu;
653 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654 if (pos != NO_SELECTED_ITEM) {
660 return NO_SELECTED_ITEM;
663 /***********************************************************************
666 static void MENU_FreeItemData( MENUITEM* item )
669 HeapFree( GetProcessHeap(), 0, item->text );
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
680 if (menu->bScrolling)
682 UINT arrow_bitmap_width, arrow_bitmap_height;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686 arrow_bitmap_width = bmp.bmWidth;
687 arrow_bitmap_height = bmp.bmHeight;
688 rect->top += arrow_bitmap_height - menu->nScrollPos;
689 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702 POINT pt, UINT *pos )
709 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
710 pt.x -= wrect.left;pt.y -= wrect.top;
712 for (i = 0; i < menu->nItems; i++, item++)
715 MENU_AdjustMenuItemRect(menu, &rect);
716 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
717 (pt.y >= rect.top) && (pt.y < rect.bottom))
727 /***********************************************************************
730 * Find the menu item selected by a key press.
731 * Return item id, -1 if none, -2 if we should close the menu.
733 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
734 WCHAR key, BOOL forceMenuChar )
736 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
738 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
742 POPUPMENU *menu = MENU_GetMenu( hmenu );
743 MENUITEM *item = menu->items;
750 for (i = 0; i < menu->nItems; i++, item++)
754 WCHAR *p = item->text - 2;
757 p = strchrW (p + 2, '&');
759 while (p != NULL && p [1] == '&');
760 if (p && (toupperW(p[1]) == toupperW(key))) return i;
764 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
765 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
766 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
767 if (HIWORD(menuchar) == 1) return (UINT)(-2);
773 /***********************************************************************
774 * MENU_GetBitmapItemSize
776 * Get the size of a bitmap item.
778 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
782 HBITMAP bmp = lpitem->hbmpItem;
784 size->cx = size->cy = 0;
786 /* check if there is a magic menu item associated with this item */
787 switch( (INT_PTR) bmp )
789 case (INT_PTR)HBMMENU_CALLBACK:
791 MEASUREITEMSTRUCT measItem;
792 measItem.CtlType = ODT_MENU;
794 measItem.itemID = lpitem->wID;
795 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
796 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
797 measItem.itemData = lpitem->dwItemData;
798 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
799 size->cx = measItem.itemWidth;
800 size->cy = measItem.itemHeight;
804 case (INT_PTR)HBMMENU_SYSTEM:
805 if (lpitem->dwItemData)
807 bmp = (HBITMAP)lpitem->dwItemData;
811 case (INT_PTR)HBMMENU_MBAR_RESTORE:
812 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
813 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
814 case (INT_PTR)HBMMENU_MBAR_CLOSE:
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
823 FIXME("Magic %p not implemented\n", bmp );
826 if (GetObjectW(bmp, sizeof(bm), &bm ))
828 size->cx = bm.bmWidth;
829 size->cy = bm.bmHeight;
833 /***********************************************************************
834 * MENU_DrawBitmapItem
836 * Draw a bitmap item.
838 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
839 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
845 int w = rect->right - rect->left;
846 int h = rect->bottom - rect->top;
849 HBITMAP hbmToDraw = lpitem->hbmpItem;
852 /* Check if there is a magic menu item associated with this item */
853 if (IS_MAGIC_BITMAP(hbmToDraw))
858 switch((INT_PTR)hbmToDraw)
860 case (INT_PTR)HBMMENU_SYSTEM:
861 if (lpitem->dwItemData)
863 bmp = (HBITMAP)lpitem->dwItemData;
864 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
868 static HBITMAP hBmpSysMenu;
870 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
872 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
873 /* only use right half of the bitmap */
874 bmp_xoffset = bm.bmWidth / 2;
875 bm.bmWidth -= bmp_xoffset;
878 case (INT_PTR)HBMMENU_MBAR_RESTORE:
879 flags = DFCS_CAPTIONRESTORE;
881 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
882 flags = DFCS_CAPTIONMIN;
884 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
885 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
887 case (INT_PTR)HBMMENU_MBAR_CLOSE:
888 flags = DFCS_CAPTIONCLOSE;
890 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
891 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
893 case (INT_PTR)HBMMENU_CALLBACK:
895 DRAWITEMSTRUCT drawItem;
896 drawItem.CtlType = ODT_MENU;
898 drawItem.itemID = lpitem->wID;
899 drawItem.itemAction = odaction;
900 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
901 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
902 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
903 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
904 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
905 drawItem.hwndItem = (HWND)hmenu;
907 drawItem.itemData = lpitem->dwItemData;
908 drawItem.rcItem = *rect;
909 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
913 case (INT_PTR)HBMMENU_POPUP_CLOSE:
914 case (INT_PTR)HBMMENU_POPUP_RESTORE:
915 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
916 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
918 FIXME("Magic %p not implemented\n", hbmToDraw);
922 InflateRect( &r, -1, -1 );
923 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
924 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
928 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
931 hdcMem = CreateCompatibleDC( hdc );
932 SelectObject( hdcMem, bmp );
934 /* handle fontsize > bitmap_height */
935 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
937 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
938 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
939 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
940 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
945 /***********************************************************************
948 * Calculate the size of the menu item and store it in lpitem->rect.
950 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
951 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
954 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
955 UINT arrow_bitmap_width;
959 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
960 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
961 (menuBar ? " (MenuBar)" : ""));
963 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
964 arrow_bitmap_width = bm.bmWidth;
966 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
967 if( !menucharsize.cx ) {
968 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
969 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
970 * but it is unlikely an application will depend on that */
971 ODitemheight = HIWORD( GetDialogBaseUnits());
974 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
976 if (lpitem->fType & MF_OWNERDRAW)
978 MEASUREITEMSTRUCT mis;
979 mis.CtlType = ODT_MENU;
981 mis.itemID = lpitem->wID;
982 mis.itemData = lpitem->dwItemData;
983 mis.itemHeight = ODitemheight;
985 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
986 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
987 * width of a menufont character to the width of an owner-drawn menu.
989 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
991 /* under at least win95 you seem to be given a standard
992 height for the menu and the height value is ignored */
993 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
995 lpitem->rect.bottom += mis.itemHeight;
997 TRACE("id=%04lx size=%dx%d\n",
998 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
999 lpitem->rect.bottom-lpitem->rect.top);
1003 if (lpitem->fType & MF_SEPARATOR)
1005 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1007 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1015 if (lpitem->hbmpItem) {
1018 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1019 /* Keep the size of the bitmap in callback mode to be able
1020 * to draw it correctly */
1021 lpitem->bmpsize = size;
1022 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1023 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1024 lpitem->rect.right += size.cx + 2;
1025 itemheight = size.cy + 2;
1027 if( !(lppop->dwStyle & MNS_NOCHECK))
1028 lpitem->rect.right += check_bitmap_width;
1029 lpitem->rect.right += 4 + menucharsize.cx;
1030 lpitem->xTab = lpitem->rect.right;
1031 lpitem->rect.right += arrow_bitmap_width;
1032 } else if (lpitem->hbmpItem) { /* menuBar */
1035 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1036 lpitem->bmpsize = size;
1037 lpitem->rect.right += size.cx;
1038 if( lpitem->text) lpitem->rect.right += 2;
1039 itemheight = size.cy;
1042 /* it must be a text item - unless it's the system menu */
1043 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1044 HFONT hfontOld = NULL;
1045 RECT rc = lpitem->rect;
1046 LONG txtheight, txtwidth;
1048 if ( lpitem->fState & MFS_DEFAULT ) {
1049 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1052 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1053 DT_SINGLELINE|DT_CALCRECT);
1054 lpitem->rect.right += rc.right - rc.left;
1055 itemheight = max( max( itemheight, txtheight),
1056 GetSystemMetrics( SM_CYMENU) - 1);
1057 lpitem->rect.right += 2 * menucharsize.cx;
1059 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1062 int n = (int)( p - lpitem->text);
1063 /* Item contains a tab (only meaningful in popup menus) */
1064 /* get text size before the tab */
1065 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1066 DT_SINGLELINE|DT_CALCRECT);
1067 txtwidth = rc.right - rc.left;
1068 p += 1; /* advance past the Tab */
1069 /* get text size after the tab */
1070 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1071 DT_SINGLELINE|DT_CALCRECT);
1072 lpitem->xTab += txtwidth;
1073 txtheight = max( txtheight, tmpheight);
1074 txtwidth += menucharsize.cx + /* space for the tab */
1075 tmprc.right - tmprc.left; /* space for the short cut */
1077 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1078 DT_SINGLELINE|DT_CALCRECT);
1079 txtwidth = rc.right - rc.left;
1080 lpitem->xTab += txtwidth;
1082 lpitem->rect.right += 2 + txtwidth;
1083 itemheight = max( itemheight,
1084 max( txtheight + 2, menucharsize.cy + 4));
1086 if (hfontOld) SelectObject (hdc, hfontOld);
1087 } else if( menuBar) {
1088 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1090 lpitem->rect.bottom += itemheight;
1091 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1095 /***********************************************************************
1096 * MENU_GetMaxPopupHeight
1099 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1102 return lppop->cyMax;
1103 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1107 /***********************************************************************
1108 * MENU_PopupMenuCalcSize
1110 * Calculate the size of a popup menu.
1112 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1117 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1119 lppop->Width = lppop->Height = 0;
1120 if (lppop->nItems == 0) return;
1123 SelectObject( hdc, get_menu_font(FALSE));
1128 lppop->maxBmpSize.cx = 0;
1129 lppop->maxBmpSize.cy = 0;
1131 while (start < lppop->nItems)
1133 lpitem = &lppop->items[start];
1135 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1136 orgX += MENU_COL_SPACE;
1137 orgY = MENU_TOP_MARGIN;
1139 maxTab = maxTabWidth = 0;
1140 /* Parse items until column break or end of menu */
1141 for (i = start; i < lppop->nItems; i++, lpitem++)
1144 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1146 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1147 maxX = max( maxX, lpitem->rect.right );
1148 orgY = lpitem->rect.bottom;
1149 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1151 maxTab = max( maxTab, lpitem->xTab );
1152 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1156 /* Finish the column (set all items to the largest width found) */
1157 maxX = max( maxX, maxTab + maxTabWidth );
1158 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1160 lpitem->rect.right = maxX;
1161 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1162 lpitem->xTab = maxTab;
1165 lppop->Height = max( lppop->Height, orgY );
1168 lppop->Width = maxX;
1170 /* space for 3d border */
1171 lppop->Height += MENU_BOTTOM_MARGIN;
1174 /* Adjust popup height if it exceeds maximum */
1175 maxHeight = MENU_GetMaxPopupHeight(lppop);
1176 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1177 if (lppop->Height >= maxHeight)
1179 lppop->Height = maxHeight;
1180 lppop->bScrolling = TRUE;
1184 lppop->bScrolling = FALSE;
1187 ReleaseDC( 0, hdc );
1191 /***********************************************************************
1192 * MENU_MenuBarCalcSize
1194 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1195 * height is off by 1 pixel which causes lengthy window relocations when
1196 * active document window is maximized/restored.
1198 * Calculate the size of the menu bar.
1200 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1201 LPPOPUPMENU lppop, HWND hwndOwner )
1204 int start, i, orgX, orgY, maxY, helpPos;
1206 if ((lprect == NULL) || (lppop == NULL)) return;
1207 if (lppop->nItems == 0) return;
1208 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1209 lppop->Width = lprect->right - lprect->left;
1211 maxY = lprect->top+1;
1214 lppop->maxBmpSize.cx = 0;
1215 lppop->maxBmpSize.cy = 0;
1216 while (start < lppop->nItems)
1218 lpitem = &lppop->items[start];
1219 orgX = lprect->left;
1222 /* Parse items until line break or end of menu */
1223 for (i = start; i < lppop->nItems; i++, lpitem++)
1225 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1227 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1229 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1230 debug_print_menuitem (" item: ", lpitem, "");
1231 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1233 if (lpitem->rect.right > lprect->right)
1235 if (i != start) break;
1236 else lpitem->rect.right = lprect->right;
1238 maxY = max( maxY, lpitem->rect.bottom );
1239 orgX = lpitem->rect.right;
1242 /* Finish the line (set all items to the largest height found) */
1243 while (start < i) lppop->items[start++].rect.bottom = maxY;
1246 lprect->bottom = maxY;
1247 lppop->Height = lprect->bottom - lprect->top;
1249 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1250 /* the last item (if several lines, only move the last line) */
1251 lpitem = &lppop->items[lppop->nItems-1];
1252 orgY = lpitem->rect.top;
1253 orgX = lprect->right;
1254 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1255 if ( (helpPos==-1) || (helpPos>i) )
1257 if (lpitem->rect.top != orgY) break; /* Other line */
1258 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1259 lpitem->rect.left += orgX - lpitem->rect.right;
1260 lpitem->rect.right = orgX;
1261 orgX = lpitem->rect.left;
1266 /***********************************************************************
1267 * MENU_DrawScrollArrows
1269 * Draw scroll arrows.
1272 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1274 HDC hdcMem = CreateCompatibleDC(hdc);
1275 HBITMAP hOrigBitmap;
1276 UINT arrow_bitmap_width, arrow_bitmap_height;
1280 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1281 arrow_bitmap_width = bmp.bmWidth;
1282 arrow_bitmap_height = bmp.bmHeight;
1285 if (lppop->nScrollPos)
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1288 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1291 rect.right = lppop->Width;
1292 rect.bottom = arrow_bitmap_height;
1293 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1294 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1295 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1296 rect.top = lppop->Height - arrow_bitmap_height;
1297 rect.bottom = lppop->Height;
1298 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1299 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1300 SelectObject(hdcMem, get_down_arrow_bitmap());
1302 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1303 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1304 lppop->Height - arrow_bitmap_height,
1305 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1306 SelectObject(hdcMem, hOrigBitmap);
1311 /***********************************************************************
1314 * Draws the popup-menu arrow.
1316 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1317 UINT arrow_bitmap_height)
1319 HDC hdcMem = CreateCompatibleDC( hdc );
1320 HBITMAP hOrigBitmap;
1322 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1323 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1324 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1325 arrow_bitmap_width, arrow_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1327 SelectObject( hdcMem, hOrigBitmap );
1330 /***********************************************************************
1333 * Draw a single menu item.
1335 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1336 UINT height, BOOL menuBar, UINT odaction )
1339 BOOL flat_menu = FALSE;
1341 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1342 POPUPMENU *menu = MENU_GetMenu(hmenu);
1345 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1349 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1350 arrow_bitmap_width = bmp.bmWidth;
1351 arrow_bitmap_height = bmp.bmHeight;
1354 if (lpitem->fType & MF_SYSMENU)
1356 if( !IsIconic(hwnd) )
1357 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1361 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1362 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1366 if (lpitem->fState & MF_HILITE)
1368 if(menuBar && !flat_menu) {
1369 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1372 if(lpitem->fState & MF_GRAYED)
1373 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1375 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1381 if (lpitem->fState & MF_GRAYED)
1382 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1384 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1385 SetBkColor( hdc, GetSysColor( bkgnd ) );
1388 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1389 rect = lpitem->rect;
1390 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1392 if (lpitem->fType & MF_OWNERDRAW)
1395 ** Experimentation under Windows reveals that an owner-drawn
1396 ** menu is given the rectangle which includes the space it requested
1397 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1398 ** and a popup-menu arrow. This is the value of lpitem->rect.
1399 ** Windows will leave all drawing to the application except for
1400 ** the popup-menu arrow. Windows always draws that itself, after
1401 ** the menu owner has finished drawing.
1405 dis.CtlType = ODT_MENU;
1407 dis.itemID = lpitem->wID;
1408 dis.itemData = lpitem->dwItemData;
1410 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1411 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1412 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1413 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1414 dis.hwndItem = (HWND)hmenu;
1417 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1418 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1419 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1420 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1421 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1422 /* Draw the popup-menu arrow */
1423 if (lpitem->fType & MF_POPUP)
1424 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1425 arrow_bitmap_height);
1429 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1431 if (lpitem->fState & MF_HILITE)
1435 InflateRect (&rect, -1, -1);
1436 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1437 InflateRect (&rect, 1, 1);
1438 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1443 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1445 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1449 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1451 SetBkMode( hdc, TRANSPARENT );
1453 /* vertical separator */
1454 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1459 rc.left -= MENU_COL_SPACE / 2 + 1;
1461 rc.bottom = height - 3;
1464 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1465 MoveToEx( hdc, rc.left, rc.top, NULL );
1466 LineTo( hdc, rc.left, rc.bottom );
1467 SelectObject( hdc, oldPen );
1470 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1473 /* horizontal separator */
1474 if (lpitem->fType & MF_SEPARATOR)
1481 rc.top = ( rc.top + rc.bottom) / 2;
1484 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1485 MoveToEx( hdc, rc.left, rc.top, NULL );
1486 LineTo( hdc, rc.right, rc.top );
1487 SelectObject( hdc, oldPen );
1490 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1494 /* helper lines for debugging */
1495 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1496 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1497 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1498 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1501 if (lpitem->hbmpItem) {
1502 /* calculate the bitmap rectangle in coordinates relative
1503 * to the item rectangle */
1505 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1508 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1511 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1512 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1514 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1515 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1518 bmprc.top = (rect.bottom - rect.top -
1519 lpitem->bmpsize.cy) / 2;
1520 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1526 INT y = rect.top + rect.bottom;
1528 int checked = FALSE;
1529 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1530 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1531 /* Draw the check mark
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu->dwStyle & MNS_NOCHECK)) {
1537 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1538 lpitem->hUnCheckBit;
1539 if (bm) /* we have a custom bitmap */
1541 HDC hdcMem = CreateCompatibleDC( hdc );
1543 SelectObject( hdcMem, bm );
1544 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1545 check_bitmap_width, check_bitmap_height,
1546 hdcMem, 0, 0, SRCCOPY );
1550 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1553 HBITMAP bm = CreateBitmap( check_bitmap_width,
1554 check_bitmap_height, 1, 1, NULL );
1555 HDC hdcMem = CreateCompatibleDC( hdc );
1557 SelectObject( hdcMem, bm );
1558 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1559 DrawFrameControl( hdcMem, &r, DFC_MENU,
1560 (lpitem->fType & MFT_RADIOCHECK) ?
1561 DFCS_MENUBULLET : DFCS_MENUCHECK );
1562 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1563 hdcMem, 0, 0, SRCCOPY );
1569 if( lpitem->hbmpItem &&
1570 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1574 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1576 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem->fType & MF_POPUP)
1580 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1581 arrow_bitmap_height);
1583 if( !(menu->dwStyle & MNS_NOCHECK))
1584 rect.left += check_bitmap_width;
1585 rect.right -= arrow_bitmap_width;
1587 else if( lpitem->hbmpItem)
1588 { /* Draw the bitmap */
1591 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1592 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1594 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1596 /* process text if present */
1602 UINT uFormat = (menuBar) ?
1603 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1604 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1606 if( !(menu->dwStyle & MNS_CHECKORBMP))
1607 rect.left += menu->maxBmpSize.cx;
1609 if ( lpitem->fState & MFS_DEFAULT )
1611 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1615 if( lpitem->hbmpItem)
1616 rect.left += lpitem->bmpsize.cx;
1617 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1618 rect.left += menucharsize.cx;
1619 rect.right -= menucharsize.cx;
1622 for (i = 0; lpitem->text[i]; i++)
1623 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1626 if(lpitem->fState & MF_GRAYED)
1628 if (!(lpitem->fState & MF_HILITE) )
1630 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1631 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1633 --rect.left; --rect.top; --rect.right; --rect.bottom;
1635 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1640 /* paint the shortcut text */
1641 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1643 if (lpitem->text[i] == '\t')
1645 rect.left = lpitem->xTab;
1646 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1650 rect.right = lpitem->xTab;
1651 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1654 if(lpitem->fState & MF_GRAYED)
1656 if (!(lpitem->fState & MF_HILITE) )
1658 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1659 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1661 --rect.left; --rect.top; --rect.right; --rect.bottom;
1663 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1669 SelectObject (hdc, hfontOld);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1681 HBRUSH hPrevBrush = 0;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1686 GetClientRect( hwnd, &rect );
1688 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1689 && (SelectObject( hdc, get_menu_font(FALSE))))
1693 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1695 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1699 BOOL flat_menu = FALSE;
1701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1703 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1705 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1707 if( (menu = MENU_GetMenu( hmenu )))
1709 /* draw menu items */
1716 for( u = menu->nItems; u > 0; u--, item++)
1717 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1718 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1720 /* draw scroll arrows */
1721 if (menu->bScrolling)
1722 MENU_DrawScrollArrows(menu, hdc);
1726 SelectObject( hdc, hPrevBrush );
1731 /***********************************************************************
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1742 HMENU hMenu = GetMenu(hwnd);
1744 lppop = MENU_GetMenu( hMenu );
1745 if (lppop == NULL || lprect == NULL)
1747 return GetSystemMetrics(SM_CYMENU);
1752 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1754 if (lppop->Height == 0)
1755 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1757 lprect->bottom = lprect->top + lppop->Height;
1759 if (hfontOld) SelectObject( hDC, hfontOld);
1760 return lppop->Height;
1763 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1767 /***********************************************************************
1770 * Display a popup menu.
1772 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1773 INT x, INT y, INT xanchor, INT yanchor )
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1784 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1785 if (menu->FocusedItem != NO_SELECTED_ITEM)
1787 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1788 menu->FocusedItem = NO_SELECTED_ITEM;
1791 /* store the owner for DrawItem */
1792 menu->hwndOwner = hwndOwner;
1794 menu->nScrollPos = 0;
1795 MENU_PopupMenuCalcSize( menu, hwndOwner );
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1800 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1802 /* FIXME: should use item rect */
1805 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1806 info.cbSize = sizeof(info);
1807 GetMonitorInfoW( monitor, &info );
1808 if( x + width > info.rcWork.right)
1810 if( xanchor && x >= width - xanchor )
1811 x -= width - xanchor;
1813 if( x + width > info.rcWork.right)
1814 x = info.rcWork.right - width;
1816 if( x < info.rcWork.left ) x = info.rcWork.left;
1818 if( y + height > info.rcWork.bottom)
1820 if( yanchor && y >= height + yanchor )
1821 y -= height + yanchor;
1823 if( y + height > info.rcWork.bottom)
1824 y = info.rcWork.bottom - height;
1826 if( y < info.rcWork.top ) y = info.rcWork.top;
1828 /* NOTE: In Windows, top menu popup is not owned. */
1829 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1830 WS_POPUP, x, y, width, height,
1831 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1833 if( !menu->hWnd ) return FALSE;
1834 if (!top_popup) top_popup = menu->hWnd;
1836 /* Display the window */
1838 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1839 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1840 UpdateWindow( menu->hWnd );
1845 /***********************************************************************
1846 * MENU_EnsureMenuItemVisible
1849 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1851 if (lppop->bScrolling)
1853 MENUITEM *item = &lppop->items[wIndex];
1854 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1855 UINT nOldPos = lppop->nScrollPos;
1857 UINT arrow_bitmap_height;
1860 GetClientRect(lppop->hWnd, &rc);
1862 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1863 arrow_bitmap_height = bmp.bmHeight;
1865 rc.top += arrow_bitmap_height;
1866 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1868 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1869 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1872 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1873 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1874 MENU_DrawScrollArrows(lppop, hdc);
1876 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1878 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1879 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1880 MENU_DrawScrollArrows(lppop, hdc);
1886 /***********************************************************************
1889 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1890 BOOL sendMenuSelect, HMENU topmenu )
1895 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1897 lppop = MENU_GetMenu( hmenu );
1898 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1900 if (lppop->FocusedItem == wIndex) return;
1901 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1902 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1903 if (!top_popup) top_popup = lppop->hWnd;
1905 SelectObject( hdc, get_menu_font(FALSE));
1907 /* Clear previous highlighted item */
1908 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1910 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1911 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1912 lppop->Height, !(lppop->wFlags & MF_POPUP),
1916 /* Highlight new item (if any) */
1917 lppop->FocusedItem = wIndex;
1918 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1920 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1921 lppop->items[wIndex].fState |= MF_HILITE;
1922 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1923 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1924 &lppop->items[wIndex], lppop->Height,
1925 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1929 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1930 SendMessageW( hwndOwner, WM_MENUSELECT,
1931 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1932 ip->fType | ip->fState |
1933 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1936 else if (sendMenuSelect) {
1939 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1940 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1941 MENUITEM *ip = &ptm->items[pos];
1942 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1943 ip->fType | ip->fState |
1944 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1948 ReleaseDC( lppop->hWnd, hdc );
1952 /***********************************************************************
1953 * MENU_MoveSelection
1955 * Moves currently selected item according to the offset parameter.
1956 * If there is no selection then it should select the last item if
1957 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1959 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1964 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1966 menu = MENU_GetMenu( hmenu );
1967 if ((!menu) || (!menu->items)) return;
1969 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1971 if( menu->nItems == 1 ) return; else
1972 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1974 if (!(menu->items[i].fType & MF_SEPARATOR))
1976 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1981 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1982 i >= 0 && i < menu->nItems ; i += offset)
1983 if (!(menu->items[i].fType & MF_SEPARATOR))
1985 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1991 /**********************************************************************
1994 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1997 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2000 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2001 TRACE("flags=%x str=%p\n", flags, str);
2003 if (IS_STRING_ITEM(flags))
2005 LPWSTR prevText = item->text;
2008 flags |= MF_SEPARATOR;
2014 /* Item beginning with a backspace is a help item */
2020 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2022 strcpyW( text, str );
2025 item->hbmpItem = NULL;
2026 HeapFree( GetProcessHeap(), 0, prevText );
2028 else if(( flags & MFT_BITMAP)) {
2029 item->hbmpItem = HBITMAP_32(LOWORD(str));
2030 /* setting bitmap clears text */
2031 HeapFree( GetProcessHeap(), 0, item->text );
2035 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2037 if (flags & MF_OWNERDRAW)
2038 item->dwItemData = (DWORD_PTR)str;
2040 item->dwItemData = 0;
2042 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2043 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2045 if (flags & MF_POPUP)
2047 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2048 if (menu) menu->wFlags |= MF_POPUP;
2060 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2062 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2063 flags |= MF_POPUP; /* keep popup */
2065 item->fType = flags & TYPE_MASK;
2066 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2067 for ModifyMenu, but Windows accepts it */
2068 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2070 /* Don't call SetRectEmpty here! */
2072 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2077 /**********************************************************************
2080 * Insert (allocate) a new item into a menu.
2082 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2087 if (!(menu = MENU_GetMenu(hMenu)))
2090 /* Find where to insert new item */
2092 if (flags & MF_BYPOSITION) {
2093 if (pos > menu->nItems)
2096 if (!MENU_FindItem( &hMenu, &pos, flags ))
2099 if (!(menu = MENU_GetMenu( hMenu )))
2104 /* Make sure that MDI system buttons stay on the right side.
2105 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2106 * regardless of their id.
2108 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2109 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2110 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2113 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2115 /* Create new items array */
2117 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2120 WARN("allocation failed\n" );
2123 if (menu->nItems > 0)
2125 /* Copy the old array into the new one */
2126 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2127 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2128 (menu->nItems-pos)*sizeof(MENUITEM) );
2129 HeapFree( GetProcessHeap(), 0, menu->items );
2131 menu->items = newItems;
2133 memset( &newItems[pos], 0, sizeof(*newItems) );
2134 menu->Height = 0; /* force size recalculate */
2135 return &newItems[pos];
2139 /**********************************************************************
2140 * MENU_ParseResource
2142 * Parse a standard menu resource and add items to the menu.
2143 * Return a pointer to the end of the resource.
2145 * NOTE: flags is equivalent to the mtOption field
2147 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2155 flags = GET_WORD(res);
2156 end_flag = flags & MF_END;
2157 /* Remove MF_END because it has the same value as MF_HILITE */
2159 res += sizeof(WORD);
2160 if (!(flags & MF_POPUP))
2163 res += sizeof(WORD);
2166 if (!unicode) res += strlen(str) + 1;
2167 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2168 if (flags & MF_POPUP)
2170 HMENU hSubMenu = CreatePopupMenu();
2171 if (!hSubMenu) return NULL;
2172 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2174 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2175 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2177 else /* Not a popup */
2179 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2180 else AppendMenuW( hMenu, flags, id,
2181 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2183 } while (!end_flag);
2188 /**********************************************************************
2189 * MENUEX_ParseResource
2191 * Parse an extended menu resource and add items to the menu.
2192 * Return a pointer to the end of the resource.
2194 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2200 mii.cbSize = sizeof(mii);
2201 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2202 mii.fType = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 mii.fState = GET_DWORD(res);
2205 res += sizeof(DWORD);
2206 mii.wID = GET_DWORD(res);
2207 res += sizeof(DWORD);
2208 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2209 res += sizeof(WORD);
2210 /* Align the text on a word boundary. */
2211 res += (~((UINT_PTR)res - 1)) & 1;
2212 mii.dwTypeData = (LPWSTR) res;
2213 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2214 /* Align the following fields on a dword boundary. */
2215 res += (~((UINT_PTR)res - 1)) & 3;
2217 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2218 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2220 if (resinfo & 1) { /* Pop-up? */
2221 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2222 res += sizeof(DWORD);
2223 mii.hSubMenu = CreatePopupMenu();
2226 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2227 DestroyMenu(mii.hSubMenu);
2230 mii.fMask |= MIIM_SUBMENU;
2231 mii.fType |= MF_POPUP;
2233 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2235 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2236 mii.wID, mii.fType);
2237 mii.fType |= MF_SEPARATOR;
2239 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2240 } while (!(resinfo & MF_END));
2245 /***********************************************************************
2248 * Return the handle of the selected sub-popup menu (if any).
2250 static HMENU MENU_GetSubPopup( HMENU hmenu )
2255 menu = MENU_GetMenu( hmenu );
2257 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2259 item = &menu->items[menu->FocusedItem];
2260 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2261 return item->hSubMenu;
2266 /***********************************************************************
2267 * MENU_HideSubPopups
2269 * Hide the sub-popup menus of this menu.
2271 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2272 BOOL sendMenuSelect )
2274 POPUPMENU *menu = MENU_GetMenu( hmenu );
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2278 if (menu && top_popup)
2284 if (menu->FocusedItem != NO_SELECTED_ITEM)
2286 item = &menu->items[menu->FocusedItem];
2287 if (!(item->fType & MF_POPUP) ||
2288 !(item->fState & MF_MOUSESELECT)) return;
2289 item->fState &= ~MF_MOUSESELECT;
2290 hsubmenu = item->hSubMenu;
2293 submenu = MENU_GetMenu( hsubmenu );
2294 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2295 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2296 DestroyWindow( submenu->hWnd );
2302 /***********************************************************************
2305 * Display the sub-menu of the selected item of this menu.
2306 * Return the handle of the submenu, or hmenu if no submenu to display.
2308 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2309 BOOL selectFirst, UINT wFlags )
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2318 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2320 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2322 item = &menu->items[menu->FocusedItem];
2323 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2326 /* message must be sent before using item,
2327 because nearly everything may be changed by the application ! */
2329 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2330 if (!(wFlags & TPM_NONOTIFY))
2331 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2332 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2334 item = &menu->items[menu->FocusedItem];
2337 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2338 if (!(item->fState & MF_HILITE))
2340 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2341 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2343 SelectObject( hdc, get_menu_font(FALSE));
2345 item->fState |= MF_HILITE;
2346 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2347 ReleaseDC( menu->hWnd, hdc );
2349 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2352 item->fState |= MF_MOUSESELECT;
2354 if (IS_SYSTEM_MENU(menu))
2356 MENU_InitSysMenuPopup(item->hSubMenu,
2357 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2358 GetClassLongW( menu->hWnd, GCL_STYLE));
2360 NC_GetSysPopupPos( menu->hWnd, &rect );
2361 rect.top = rect.bottom;
2362 rect.right = GetSystemMetrics(SM_CXSIZE);
2363 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2367 GetWindowRect( menu->hWnd, &rect );
2368 if (menu->wFlags & MF_POPUP)
2370 RECT rc = item->rect;
2372 MENU_AdjustMenuItemRect(menu, &rc);
2374 /* The first item in the popup menu has to be at the
2375 same y position as the focused menu item */
2376 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2377 rect.top += rc.top - MENU_TOP_MARGIN;
2378 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2379 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2380 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2384 rect.left += item->rect.left;
2385 rect.top += item->rect.bottom;
2386 rect.right = item->rect.right - item->rect.left;
2387 rect.bottom = item->rect.bottom - item->rect.top;
2391 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2392 rect.left, rect.top, rect.right, rect.bottom );
2394 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2395 return item->hSubMenu;
2400 /**********************************************************************
2403 HWND MENU_IsMenuActive(void)
2408 /***********************************************************************
2411 * Walks menu chain trying to find a menu pt maps to.
2413 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2415 POPUPMENU *menu = MENU_GetMenu( hMenu );
2416 UINT item = menu->FocusedItem;
2419 /* try subpopup first (if any) */
2420 ret = (item != NO_SELECTED_ITEM &&
2421 (menu->items[item].fType & MF_POPUP) &&
2422 (menu->items[item].fState & MF_MOUSESELECT))
2423 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2425 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2427 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2428 if( menu->wFlags & MF_POPUP )
2430 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2432 else if (ht == HTSYSMENU)
2433 ret = get_win_sys_menu( menu->hWnd );
2434 else if (ht == HTMENU)
2435 ret = GetMenu( menu->hWnd );
2440 /***********************************************************************
2441 * MENU_ExecFocusedItem
2443 * Execute a menu item (for instance when user pressed Enter).
2444 * Return the wID of the executed item. Otherwise, -1 indicating
2445 * that no menu item was executed, -2 if a popup is shown;
2446 * Have to receive the flags for the TrackPopupMenu options to avoid
2447 * sending unwanted message.
2450 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2453 POPUPMENU *menu = MENU_GetMenu( hMenu );
2455 TRACE("%p hmenu=%p\n", pmt, hMenu);
2457 if (!menu || !menu->nItems ||
2458 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2460 item = &menu->items[menu->FocusedItem];
2462 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2464 if (!(item->fType & MF_POPUP))
2466 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2468 /* If TPM_RETURNCMD is set you return the id, but
2469 do not send a message to the owner */
2470 if(!(wFlags & TPM_RETURNCMD))
2472 if( menu->wFlags & MF_SYSMENU )
2473 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2474 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2477 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2478 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2481 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2489 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2496 /***********************************************************************
2497 * MENU_SwitchTracking
2499 * Helper function for menu navigation routines.
2501 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2503 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2506 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2508 if( pmt->hTopMenu != hPtMenu &&
2509 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2511 /* both are top level menus (system and menu-bar) */
2512 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2513 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2514 pmt->hTopMenu = hPtMenu;
2516 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2517 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2521 /***********************************************************************
2524 * Return TRUE if we can go on with menu tracking.
2526 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2528 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2536 if( IS_SYSTEM_MENU(ptmenu) )
2537 item = ptmenu->items;
2539 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2543 if( ptmenu->FocusedItem != id )
2544 MENU_SwitchTracking( pmt, hPtMenu, id );
2546 /* If the popup menu is not already "popped" */
2547 if(!(item->fState & MF_MOUSESELECT ))
2549 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2554 /* Else the click was on the menu bar, finish the tracking */
2559 /***********************************************************************
2562 * Return the value of MENU_ExecFocusedItem if
2563 * the selected item was not a popup. Else open the popup.
2564 * A -1 return value indicates that we go on with menu tracking.
2567 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2569 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2574 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2577 if( IS_SYSTEM_MENU(ptmenu) )
2578 item = ptmenu->items;
2580 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2582 if( item && (ptmenu->FocusedItem == id ))
2584 debug_print_menuitem ("FocusedItem: ", item, "");
2586 if( !(item->fType & MF_POPUP) )
2588 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2589 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2590 return executedMenuId;
2593 /* If we are dealing with the top-level menu */
2594 /* and this is a click on an already "popped" item: */
2595 /* Stop the menu tracking and close the opened submenus */
2596 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2599 ptmenu->bTimeToHide = TRUE;
2605 /***********************************************************************
2608 * Return TRUE if we can go on with menu tracking.
2610 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2612 UINT id = NO_SELECTED_ITEM;
2613 POPUPMENU *ptmenu = NULL;
2617 ptmenu = MENU_GetMenu( hPtMenu );
2618 if( IS_SYSTEM_MENU(ptmenu) )
2621 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2624 if( id == NO_SELECTED_ITEM )
2626 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2627 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2630 else if( ptmenu->FocusedItem != id )
2632 MENU_SwitchTracking( pmt, hPtMenu, id );
2633 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2639 /***********************************************************************
2642 static void MENU_SetCapture( HWND hwnd )
2646 SERVER_START_REQ( set_capture_window )
2649 req->flags = CAPTURE_MENU;
2650 if (!wine_server_call_err( req ))
2652 previous = reply->previous;
2653 hwnd = reply->full_handle;
2658 if (previous && previous != hwnd)
2659 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2663 /***********************************************************************
2666 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2668 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2670 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2672 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2673 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2675 MDINEXTMENU next_menu;
2680 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2681 next_menu.hmenuNext = 0;
2682 next_menu.hwndNext = 0;
2683 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2685 TRACE("%p [%p] -> %p [%p]\n",
2686 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2688 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2690 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2691 hNewWnd = pmt->hOwnerWnd;
2692 if( IS_SYSTEM_MENU(menu) )
2694 /* switch to the menu bar */
2696 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2700 menu = MENU_GetMenu( hNewMenu );
2701 id = menu->nItems - 1;
2704 else if (style & WS_SYSMENU )
2706 /* switch to the system menu */
2707 hNewMenu = get_win_sys_menu( hNewWnd );
2711 else /* application returned a new menu to switch to */
2713 hNewMenu = next_menu.hmenuNext;
2714 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2716 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2718 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2720 if (style & WS_SYSMENU &&
2721 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2723 /* get the real system menu */
2724 hNewMenu = get_win_sys_menu(hNewWnd);
2726 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2728 /* FIXME: Not sure what to do here;
2729 * perhaps try to track hNewMenu as a popup? */
2731 TRACE(" -- got confused.\n");
2738 if( hNewMenu != pmt->hTopMenu )
2740 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2742 if( pmt->hCurrentMenu != pmt->hTopMenu )
2743 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2746 if( hNewWnd != pmt->hOwnerWnd )
2748 pmt->hOwnerWnd = hNewWnd;
2749 MENU_SetCapture( pmt->hOwnerWnd );
2752 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2753 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2760 /***********************************************************************
2763 * The idea is not to show the popup if the next input message is
2764 * going to hide it anyway.
2766 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2770 msg.hwnd = pmt->hOwnerWnd;
2772 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2773 pmt->trackFlags |= TF_SKIPREMOVE;
2778 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2779 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2781 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2782 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2783 if( msg.message == WM_KEYDOWN &&
2784 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2786 pmt->trackFlags |= TF_SUSPENDPOPUP;
2793 /* failures go through this */
2794 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2798 /***********************************************************************
2801 * Handle a VK_ESCAPE key event in a menu.
2803 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2805 BOOL bEndMenu = TRUE;
2807 if (pmt->hCurrentMenu != pmt->hTopMenu)
2809 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2811 if (menu->wFlags & MF_POPUP)
2813 HMENU hmenutmp, hmenuprev;
2815 hmenuprev = hmenutmp = pmt->hTopMenu;
2817 /* close topmost popup */
2818 while (hmenutmp != pmt->hCurrentMenu)
2820 hmenuprev = hmenutmp;
2821 hmenutmp = MENU_GetSubPopup( hmenuprev );
2824 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2825 pmt->hCurrentMenu = hmenuprev;
2833 /***********************************************************************
2836 * Handle a VK_LEFT key event in a menu.
2838 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2841 HMENU hmenutmp, hmenuprev;
2844 hmenuprev = hmenutmp = pmt->hTopMenu;
2845 menu = MENU_GetMenu( hmenutmp );
2847 /* Try to move 1 column left (if possible) */
2848 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2849 NO_SELECTED_ITEM ) {
2851 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2856 /* close topmost popup */
2857 while (hmenutmp != pmt->hCurrentMenu)
2859 hmenuprev = hmenutmp;
2860 hmenutmp = MENU_GetSubPopup( hmenuprev );
2863 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2864 pmt->hCurrentMenu = hmenuprev;
2866 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2868 /* move menu bar selection if no more popups are left */
2870 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2871 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2873 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2875 /* A sublevel menu was displayed - display the next one
2876 * unless there is another displacement coming up */
2878 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2879 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2880 pmt->hTopMenu, TRUE, wFlags);
2886 /***********************************************************************
2889 * Handle a VK_RIGHT key event in a menu.
2891 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2894 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2897 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2899 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2900 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2902 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2904 /* If already displaying a popup, try to display sub-popup */
2906 hmenutmp = pmt->hCurrentMenu;
2907 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2909 /* if subpopup was displayed then we are done */
2910 if (hmenutmp != pmt->hCurrentMenu) return;
2913 /* Check to see if there's another column */
2914 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2915 NO_SELECTED_ITEM ) {
2916 TRACE("Going to %d.\n", nextcol );
2917 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2922 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2924 if( pmt->hCurrentMenu != pmt->hTopMenu )
2926 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2927 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2928 } else hmenutmp = 0;
2930 /* try to move to the next item */
2931 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2932 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2934 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2935 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2936 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2937 pmt->hTopMenu, TRUE, wFlags);
2941 /***********************************************************************
2944 * Menu tracking code.
2946 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2947 HWND hwnd, const RECT *lprect )
2952 INT executedMenuId = -1;
2954 BOOL enterIdleSent = FALSE;
2957 mt.hCurrentMenu = hmenu;
2958 mt.hTopMenu = hmenu;
2959 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2963 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2964 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2967 if (!(menu = MENU_GetMenu( hmenu )))
2969 WARN("Invalid menu handle %p\n", hmenu);
2970 SetLastError(ERROR_INVALID_MENU_HANDLE);
2974 if (wFlags & TPM_BUTTONDOWN)
2976 /* Get the result in order to start the tracking or not */
2977 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2978 fEndMenu = !fRemove;
2981 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2983 MENU_SetCapture( mt.hOwnerWnd );
2987 menu = MENU_GetMenu( mt.hCurrentMenu );
2988 if (!menu) /* sometimes happens if I do a window manager close */
2991 /* we have to keep the message in the queue until it's
2992 * clear that menu loop is not over yet. */
2996 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2998 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2999 /* remove the message from the queue */
3000 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3006 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3007 enterIdleSent = TRUE;
3008 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3014 /* check if EndMenu() tried to cancel us, by posting this message */
3015 if(msg.message == WM_CANCELMODE)
3017 /* we are now out of the loop */
3020 /* remove the message from the queue */
3021 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3023 /* break out of internal loop, ala ESCAPE */
3027 TranslateMessage( &msg );
3030 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3031 enterIdleSent=FALSE;
3034 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3037 * Use the mouse coordinates in lParam instead of those in the MSG
3038 * struct to properly handle synthetic messages. They are already
3039 * in screen coordinates.
3041 mt.pt.x = (short)LOWORD(msg.lParam);
3042 mt.pt.y = (short)HIWORD(msg.lParam);
3044 /* Find a menu for this mouse event */
3045 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3049 /* no WM_NC... messages in captured state */
3051 case WM_RBUTTONDBLCLK:
3052 case WM_RBUTTONDOWN:
3053 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3055 case WM_LBUTTONDBLCLK:
3056 case WM_LBUTTONDOWN:
3057 /* If the message belongs to the menu, removes it from the queue */
3058 /* Else, end menu tracking */
3059 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3060 fEndMenu = !fRemove;
3064 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3067 /* Check if a menu was selected by the mouse */
3070 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3071 TRACE("executedMenuId %d\n", executedMenuId);
3073 /* End the loop if executedMenuId is an item ID */
3074 /* or if the job was done (executedMenuId = 0). */
3075 fEndMenu = fRemove = (executedMenuId != -1);
3077 /* No menu was selected by the mouse */
3078 /* if the function was called by TrackPopupMenu, continue
3079 with the menu tracking. If not, stop it */
3081 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3086 /* the selected menu item must be changed every time */
3087 /* the mouse moves. */
3090 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3092 } /* switch(msg.message) - mouse */
3094 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3096 fRemove = TRUE; /* Keyboard messages are always removed */
3109 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3110 NO_SELECTED_ITEM, FALSE, 0 );
3111 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3112 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3116 case VK_DOWN: /* If on menu bar, pull-down the menu */
3118 menu = MENU_GetMenu( mt.hCurrentMenu );
3119 if (!(menu->wFlags & MF_POPUP))
3120 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3121 else /* otherwise try to move selection */
3122 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3123 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3127 MENU_KeyLeft( &mt, wFlags );
3131 MENU_KeyRight( &mt, wFlags );
3135 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3141 hi.cbSize = sizeof(HELPINFO);
3142 hi.iContextType = HELPINFO_MENUITEM;
3143 if (menu->FocusedItem == NO_SELECTED_ITEM)
3146 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3147 hi.hItemHandle = hmenu;
3148 hi.dwContextId = menu->dwContextHelpID;
3149 hi.MousePos = msg.pt;
3150 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3157 break; /* WM_KEYDOWN */
3164 if (msg.wParam == '\r' || msg.wParam == ' ')
3166 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3167 fEndMenu = (executedMenuId != -2);
3172 /* Hack to avoid control chars. */
3173 /* We will find a better way real soon... */
3174 if (msg.wParam < 32) break;
3176 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3177 LOWORD(msg.wParam), FALSE );
3178 if (pos == (UINT)-2) fEndMenu = TRUE;
3179 else if (pos == (UINT)-1) MessageBeep(0);
3182 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3184 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3185 fEndMenu = (executedMenuId != -2);
3189 } /* switch(msg.message) - kbd */
3193 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3194 DispatchMessageW( &msg );
3198 if (!fEndMenu) fRemove = TRUE;
3200 /* finally remove message from the queue */
3202 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3203 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3204 else mt.trackFlags &= ~TF_SKIPREMOVE;
3207 MENU_SetCapture(0); /* release the capture */
3209 /* If dropdown is still painted and the close box is clicked on
3210 then the menu will be destroyed as part of the DispatchMessage above.
3211 This will then invalidate the menu handle in mt.hTopMenu. We should
3212 check for this first. */
3213 if( IsMenu( mt.hTopMenu ) )
3215 menu = MENU_GetMenu( mt.hTopMenu );
3217 if( IsWindow( mt.hOwnerWnd ) )
3219 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3221 if (menu && (menu->wFlags & MF_POPUP))
3223 DestroyWindow( menu->hWnd );
3226 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3227 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3230 /* Reset the variable for hiding menu */
3231 if( menu ) menu->bTimeToHide = FALSE;
3234 /* The return value is only used by TrackPopupMenu */
3235 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3236 if (executedMenuId == -1) executedMenuId = 0;
3237 return executedMenuId;
3240 /***********************************************************************
3243 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3247 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3251 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3252 if (!(wFlags & TPM_NONOTIFY))
3253 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3255 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3257 if (!(wFlags & TPM_NONOTIFY))
3259 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3260 /* If an app changed/recreated menu bar entries in WM_INITMENU
3261 * menu sizes will be recalculated once the menu created/shown.
3265 /* This makes the menus of applications built with Delphi work.
3266 * It also enables menus to be displayed in more than one window,
3267 * but there are some bugs left that need to be fixed in this case.
3269 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3273 /***********************************************************************
3276 static BOOL MENU_ExitTracking(HWND hWnd)
3278 TRACE("hwnd=%p\n", hWnd);
3280 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3286 /***********************************************************************
3287 * MENU_TrackMouseMenuBar
3289 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3291 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3293 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3294 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3296 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3300 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3301 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3302 MENU_ExitTracking(hWnd);
3307 /***********************************************************************
3308 * MENU_TrackKbdMenuBar
3310 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3312 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3314 UINT uItem = NO_SELECTED_ITEM;
3316 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3318 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3320 /* find window that has a menu */
3322 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3323 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3325 /* check if we have to track a system menu */
3327 hTrackMenu = GetMenu( hwnd );
3328 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3330 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3331 hTrackMenu = get_win_sys_menu( hwnd );
3333 wParam |= HTSYSMENU; /* prevent item lookup */
3336 if (!IsMenu( hTrackMenu )) return;
3338 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3340 if( wChar && wChar != ' ' )
3342 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3343 if ( uItem >= (UINT)(-2) )
3345 if( uItem == (UINT)(-1) ) MessageBeep(0);
3346 /* schedule end of menu tracking */
3347 wFlags |= TF_ENDMENU;
3352 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3354 if (wParam & HTSYSMENU)
3356 /* prevent sysmenu activation for managed windows on Alt down/up */
3357 if (GetPropA( hwnd, "__wine_x11_managed" ))
3358 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3362 if( uItem == NO_SELECTED_ITEM )
3363 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3365 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3369 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3370 MENU_ExitTracking( hwnd );
3374 /**********************************************************************
3375 * TrackPopupMenu (USER32.@)
3377 * Like the win32 API, the function return the command ID only if the
3378 * flag TPM_RETURNCMD is on.
3381 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3382 INT nReserved, HWND hWnd, const RECT *lpRect )
3386 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3387 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3389 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3391 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3392 if (!(wFlags & TPM_NONOTIFY))
3393 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3395 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3396 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3397 MENU_ExitTracking(hWnd);
3402 /**********************************************************************
3403 * TrackPopupMenuEx (USER32.@)
3405 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3406 HWND hWnd, LPTPMPARAMS lpTpm )
3408 FIXME("not fully implemented\n" );
3409 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3410 lpTpm ? &lpTpm->rcExclude : NULL );
3413 /***********************************************************************
3416 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3418 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3420 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3426 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3427 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3431 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3432 return MA_NOACTIVATE;
3437 BeginPaint( hwnd, &ps );
3438 MENU_DrawPopupMenu( hwnd, ps.hdc,
3439 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3440 EndPaint( hwnd, &ps );
3447 /* zero out global pointer in case resident popup window was destroyed. */
3448 if (hwnd == top_popup) top_popup = 0;
3455 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3458 SetWindowLongPtrW( hwnd, 0, 0 );
3461 case MM_SETMENUHANDLE:
3462 SetWindowLongPtrW( hwnd, 0, wParam );
3465 case MM_GETMENUHANDLE:
3466 return GetWindowLongPtrW( hwnd, 0 );
3469 return DefWindowProcW( hwnd, message, wParam, lParam );
3475 /***********************************************************************
3476 * MENU_GetMenuBarHeight
3478 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3480 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3481 INT orgX, INT orgY )
3487 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3489 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3491 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3492 SelectObject( hdc, get_menu_font(FALSE));
3493 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3494 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3495 ReleaseDC( hwnd, hdc );
3496 return lppop->Height;
3500 /*******************************************************************
3501 * ChangeMenuA (USER32.@)
3503 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3504 UINT id, UINT flags )
3506 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3507 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3509 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3510 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3512 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3513 flags & MF_BYPOSITION ? pos : id,
3514 flags & ~MF_REMOVE );
3515 /* Default: MF_INSERT */
3516 return InsertMenuA( hMenu, pos, flags, id, data );
3520 /*******************************************************************
3521 * ChangeMenuW (USER32.@)
3523 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3524 UINT id, UINT flags )
3526 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3527 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3529 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3530 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3532 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3533 flags & MF_BYPOSITION ? pos : id,
3534 flags & ~MF_REMOVE );
3535 /* Default: MF_INSERT */
3536 return InsertMenuW( hMenu, pos, flags, id, data );
3540 /*******************************************************************
3541 * CheckMenuItem (USER32.@)
3543 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3548 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3549 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3550 ret = item->fState & MF_CHECKED;
3551 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3552 else item->fState &= ~MF_CHECKED;
3557 /**********************************************************************
3558 * EnableMenuItem (USER32.@)
3560 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3566 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3568 /* Get the Popupmenu to access the owner menu */
3569 if (!(menu = MENU_GetMenu(hMenu)))
3572 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3575 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3576 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3578 /* If the close item in the system menu change update the close button */
3579 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3581 if (menu->hSysMenuOwner != 0)
3584 POPUPMENU* parentMenu;
3586 /* Get the parent menu to access*/
3587 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3590 /* Refresh the frame to reflect the change */
3591 GetWindowRect(parentMenu->hWnd, &rc);
3592 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3594 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3602 /*******************************************************************
3603 * GetMenuStringA (USER32.@)
3605 INT WINAPI GetMenuStringA(
3606 HMENU hMenu, /* [in] menuhandle */
3607 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3608 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3609 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3610 UINT wFlags /* [in] MF_ flags */
3614 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3615 if (str && nMaxSiz) str[0] = '\0';
3616 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3617 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3620 if (!item->text) return 0;
3621 if (!str || !nMaxSiz) return strlenW(item->text);
3622 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3624 TRACE("returning '%s'\n", str );
3629 /*******************************************************************
3630 * GetMenuStringW (USER32.@)
3632 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3633 LPWSTR str, INT nMaxSiz, UINT wFlags )
3637 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3638 if (str && nMaxSiz) str[0] = '\0';
3639 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3640 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3643 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3644 if( !(item->text)) {
3648 lstrcpynW( str, item->text, nMaxSiz );
3649 return strlenW(str);
3653 /**********************************************************************
3654 * HiliteMenuItem (USER32.@)
3656 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3660 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3661 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3662 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3663 if (menu->FocusedItem == wItemID) return TRUE;
3664 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3665 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3670 /**********************************************************************
3671 * GetMenuState (USER32.@)
3673 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3676 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3677 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3678 debug_print_menuitem (" item: ", item, "");
3679 if (item->fType & MF_POPUP)
3681 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3682 if (!menu) return -1;
3683 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3687 /* We used to (from way back then) mask the result to 0xff. */
3688 /* I don't know why and it seems wrong as the documented */
3689 /* return flag MF_SEPARATOR is outside that mask. */
3690 return (item->fType | item->fState);
3695 /**********************************************************************
3696 * GetMenuItemCount (USER32.@)
3698 INT WINAPI GetMenuItemCount( HMENU hMenu )
3700 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3701 if (!menu) return -1;
3702 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3703 return menu->nItems;
3707 /**********************************************************************
3708 * GetMenuItemID (USER32.@)
3710 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3714 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3715 if (lpmi->fType & MF_POPUP) return -1;
3721 /*******************************************************************
3722 * InsertMenuW (USER32.@)
3724 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3725 UINT_PTR id, LPCWSTR str )
3729 if (IS_STRING_ITEM(flags) && str)
3730 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3731 hMenu, pos, flags, id, debugstr_w(str) );
3732 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3733 hMenu, pos, flags, id, str );
3735 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3737 if (!(MENU_SetItemData( item, flags, id, str )))
3739 RemoveMenu( hMenu, pos, flags );
3743 item->hCheckBit = item->hUnCheckBit = 0;
3748 /*******************************************************************
3749 * InsertMenuA (USER32.@)
3751 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3752 UINT_PTR id, LPCSTR str )
3756 if (IS_STRING_ITEM(flags) && str)
3758 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3759 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3762 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3763 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3764 HeapFree( GetProcessHeap(), 0, newstr );
3768 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3772 /*******************************************************************
3773 * AppendMenuA (USER32.@)
3775 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3776 UINT_PTR id, LPCSTR data )
3778 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3782 /*******************************************************************
3783 * AppendMenuW (USER32.@)
3785 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3786 UINT_PTR id, LPCWSTR data )
3788 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3792 /**********************************************************************
3793 * RemoveMenu (USER32.@)
3795 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3800 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3801 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3802 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3806 MENU_FreeItemData( item );
3808 if (--menu->nItems == 0)
3810 HeapFree( GetProcessHeap(), 0, menu->items );
3815 while(nPos < menu->nItems)
3821 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3822 menu->nItems * sizeof(MENUITEM) );
3828 /**********************************************************************
3829 * DeleteMenu (USER32.@)
3831 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3833 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3834 if (!item) return FALSE;
3835 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3836 /* nPos is now the position of the item */
3837 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3842 /*******************************************************************
3843 * ModifyMenuW (USER32.@)
3845 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3846 UINT_PTR id, LPCWSTR str )
3850 if (IS_STRING_ITEM(flags))
3851 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3853 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3855 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3856 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3857 return MENU_SetItemData( item, flags, id, str );
3861 /*******************************************************************
3862 * ModifyMenuA (USER32.@)
3864 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3865 UINT_PTR id, LPCSTR str )
3869 if (IS_STRING_ITEM(flags) && str)
3871 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3872 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3875 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3876 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3877 HeapFree( GetProcessHeap(), 0, newstr );
3881 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3885 /**********************************************************************
3886 * CreatePopupMenu (USER32.@)
3888 HMENU WINAPI CreatePopupMenu(void)
3893 if (!(hmenu = CreateMenu())) return 0;
3894 menu = MENU_GetMenu( hmenu );
3895 menu->wFlags |= MF_POPUP;
3896 menu->bTimeToHide = FALSE;
3901 /**********************************************************************
3902 * GetMenuCheckMarkDimensions (USER.417)
3903 * GetMenuCheckMarkDimensions (USER32.@)
3905 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3907 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3911 /**********************************************************************
3912 * SetMenuItemBitmaps (USER32.@)
3914 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3915 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3918 TRACE("(%p, %04x, %04x, %p, %p)\n",
3919 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3920 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3922 if (!hNewCheck && !hNewUnCheck)
3924 item->fState &= ~MF_USECHECKBITMAPS;
3926 else /* Install new bitmaps */
3928 item->hCheckBit = hNewCheck;
3929 item->hUnCheckBit = hNewUnCheck;
3930 item->fState |= MF_USECHECKBITMAPS;
3936 /**********************************************************************
3937 * CreateMenu (USER32.@)
3939 HMENU WINAPI CreateMenu(void)
3943 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3944 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3946 ZeroMemory(menu, sizeof(POPUPMENU));
3947 menu->wMagic = MENU_MAGIC;
3948 menu->FocusedItem = NO_SELECTED_ITEM;
3949 menu->bTimeToHide = FALSE;
3951 TRACE("return %p\n", hMenu );
3957 /**********************************************************************
3958 * DestroyMenu (USER32.@)
3960 BOOL WINAPI DestroyMenu( HMENU hMenu )
3962 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3964 TRACE("(%p)\n", hMenu);
3967 if (!lppop) return FALSE;
3969 lppop->wMagic = 0; /* Mark it as destroyed */
3971 /* DestroyMenu should not destroy system menu popup owner */
3972 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3974 DestroyWindow( lppop->hWnd );
3978 if (lppop->items) /* recursively destroy submenus */
3981 MENUITEM *item = lppop->items;
3982 for (i = lppop->nItems; i > 0; i--, item++)
3984 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3985 MENU_FreeItemData( item );
3987 HeapFree( GetProcessHeap(), 0, lppop->items );
3989 USER_HEAP_FREE( hMenu );
3994 /**********************************************************************
3995 * GetSystemMenu (USER32.@)
3997 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3999 WND *wndPtr = WIN_GetPtr( hWnd );
4002 if (wndPtr == WND_DESKTOP) return 0;
4003 if (wndPtr == WND_OTHER_PROCESS)
4005 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4009 if (wndPtr->hSysMenu && bRevert)
4011 DestroyMenu(wndPtr->hSysMenu);
4012 wndPtr->hSysMenu = 0;
4015 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4016 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4018 if( wndPtr->hSysMenu )
4021 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4023 /* Store the dummy sysmenu handle to facilitate the refresh */
4024 /* of the close button if the SC_CLOSE item change */
4025 menu = MENU_GetMenu(retvalue);
4027 menu->hSysMenuOwner = wndPtr->hSysMenu;
4029 WIN_ReleasePtr( wndPtr );
4031 return bRevert ? 0 : retvalue;
4035 /*******************************************************************
4036 * SetSystemMenu (USER32.@)
4038 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4040 WND *wndPtr = WIN_GetPtr( hwnd );
4042 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4044 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4045 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4046 WIN_ReleasePtr( wndPtr );
4053 /**********************************************************************
4054 * GetMenu (USER32.@)
4056 HMENU WINAPI GetMenu( HWND hWnd )
4058 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4059 TRACE("for %p returning %p\n", hWnd, retvalue);
4063 /**********************************************************************
4064 * GetMenuBarInfo (USER32.@)
4066 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4068 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4072 /**********************************************************************
4075 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4076 * SetWindowPos call that would result if SetMenu were called directly.
4078 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4080 TRACE("(%p, %p);\n", hWnd, hMenu);
4082 if (hMenu && !IsMenu(hMenu))
4084 WARN("hMenu %p is not a menu handle\n", hMenu);
4087 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4090 hWnd = WIN_GetFullHandle( hWnd );
4091 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4097 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4099 lpmenu->hWnd = hWnd;
4100 lpmenu->Height = 0; /* Make sure we recalculate the size */
4102 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4107 /**********************************************************************
4108 * SetMenu (USER32.@)
4110 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4112 if(!MENU_SetMenu(hWnd, hMenu))
4115 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4116 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4121 /**********************************************************************
4122 * GetSubMenu (USER32.@)
4124 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4128 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4129 if (!(lpmi->fType & MF_POPUP)) return 0;
4130 return lpmi->hSubMenu;
4134 /**********************************************************************
4135 * DrawMenuBar (USER32.@)
4137 BOOL WINAPI DrawMenuBar( HWND hWnd )
4140 HMENU hMenu = GetMenu(hWnd);
4142 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4144 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4146 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4147 lppop->hwndOwner = hWnd;
4148 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4149 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4153 /***********************************************************************
4154 * DrawMenuBarTemp (USER32.@)
4158 * called by W98SE desk.cpl Control Panel Applet
4160 * Not 100% sure about the param names, but close.
4162 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4167 BOOL flat_menu = FALSE;
4169 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4172 hMenu = GetMenu(hwnd);
4175 hFont = get_menu_font(FALSE);
4177 lppop = MENU_GetMenu( hMenu );
4178 if (lppop == NULL || lprect == NULL)
4180 retvalue = GetSystemMetrics(SM_CYMENU);
4184 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4186 hfontOld = SelectObject( hDC, hFont);
4188 if (lppop->Height == 0)
4189 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4191 lprect->bottom = lprect->top + lppop->Height;
4193 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4195 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4196 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4197 LineTo( hDC, lprect->right, lprect->bottom );
4199 if (lppop->nItems == 0)
4201 retvalue = GetSystemMetrics(SM_CYMENU);
4205 for (i = 0; i < lppop->nItems; i++)
4207 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4208 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4210 retvalue = lppop->Height;
4213 if (hfontOld) SelectObject (hDC, hfontOld);
4217 /***********************************************************************
4218 * EndMenu (USER.187)
4219 * EndMenu (USER32.@)
4221 void WINAPI EndMenu(void)
4223 /* if we are in the menu code, and it is active */
4224 if (!fEndMenu && top_popup)
4226 /* terminate the menu handling code */
4229 /* needs to be posted to wakeup the internal menu handler */
4230 /* which will now terminate the menu, in the event that */
4231 /* the main window was minimized, or lost focus, so we */
4232 /* don't end up with an orphaned menu */
4233 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4238 /***********************************************************************
4239 * LookupMenuHandle (USER.217)
4241 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4243 HMENU hmenu32 = HMENU_32(hmenu);
4245 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4246 else return HMENU_16(hmenu32);
4250 /**********************************************************************
4251 * LoadMenu (USER.150)
4253 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4259 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4260 if (!name) return 0;
4262 instance = GetExePtr( instance );
4263 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4264 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4265 hMenu = LoadMenuIndirect16(LockResource16(handle));
4266 FreeResource16( handle );
4271 /*****************************************************************
4272 * LoadMenuA (USER32.@)
4274 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4276 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4277 if (!hrsrc) return 0;
4278 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4282 /*****************************************************************
4283 * LoadMenuW (USER32.@)
4285 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4287 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4288 if (!hrsrc) return 0;
4289 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4293 /**********************************************************************
4294 * LoadMenuIndirect (USER.220)
4296 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4299 WORD version, offset;
4300 LPCSTR p = (LPCSTR)template;
4302 TRACE("(%p)\n", template );
4303 version = GET_WORD(p);
4307 WARN("version must be 0 for Win16\n" );
4310 offset = GET_WORD(p);
4311 p += sizeof(WORD) + offset;
4312 if (!(hMenu = CreateMenu())) return 0;
4313 if (!MENU_ParseResource( p, hMenu, FALSE ))
4315 DestroyMenu( hMenu );
4318 return HMENU_16(hMenu);
4322 /**********************************************************************
4323 * LoadMenuIndirectW (USER32.@)
4325 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4328 WORD version, offset;
4329 LPCSTR p = (LPCSTR)template;
4331 version = GET_WORD(p);
4333 TRACE("%p, ver %d\n", template, version );
4336 case 0: /* standard format is version of 0 */
4337 offset = GET_WORD(p);
4338 p += sizeof(WORD) + offset;
4339 if (!(hMenu = CreateMenu())) return 0;
4340 if (!MENU_ParseResource( p, hMenu, TRUE ))
4342 DestroyMenu( hMenu );
4346 case 1: /* extended format is version of 1 */
4347 offset = GET_WORD(p);
4348 p += sizeof(WORD) + offset;
4349 if (!(hMenu = CreateMenu())) return 0;
4350 if (!MENUEX_ParseResource( p, hMenu))
4352 DestroyMenu( hMenu );
4357 ERR("version %d not supported.\n", version);
4363 /**********************************************************************
4364 * LoadMenuIndirectA (USER32.@)
4366 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4368 return LoadMenuIndirectW( template );
4372 /**********************************************************************
4375 BOOL WINAPI IsMenu(HMENU hmenu)
4377 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4381 SetLastError(ERROR_INVALID_MENU_HANDLE);
4387 /**********************************************************************
4388 * GetMenuItemInfo_common
4391 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4392 LPMENUITEMINFOW lpmii, BOOL unicode)
4394 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4396 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4401 if( lpmii->fMask & MIIM_TYPE) {
4402 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4403 WARN("invalid combination of fMask bits used\n");
4404 /* this does not happen on Win9x/ME */
4405 SetLastError( ERROR_INVALID_PARAMETER);
4408 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4409 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4410 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4411 if( lpmii->fType & MFT_BITMAP) {
4412 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4414 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4415 /* this does not happen on Win9x/ME */
4416 lpmii->dwTypeData = 0;
4421 /* copy the text string */
4422 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4424 if(lpmii->dwTypeData && lpmii->cch) {
4427 *((WCHAR *)lpmii->dwTypeData) = 0;
4429 *((CHAR *)lpmii->dwTypeData) = 0;
4435 len = strlenW(menu->text);
4436 if(lpmii->dwTypeData && lpmii->cch)
4437 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4441 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4442 0, NULL, NULL ) - 1;
4443 if(lpmii->dwTypeData && lpmii->cch)
4444 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4445 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4446 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4448 /* if we've copied a substring we return its length */
4449 if(lpmii->dwTypeData && lpmii->cch)
4450 if (lpmii->cch <= len + 1)
4455 /* return length of string */
4456 /* not on Win9x/ME if fType & MFT_BITMAP */
4462 if (lpmii->fMask & MIIM_FTYPE)
4463 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4465 if (lpmii->fMask & MIIM_BITMAP)
4466 lpmii->hbmpItem = menu->hbmpItem;
4468 if (lpmii->fMask & MIIM_STATE)
4469 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4471 if (lpmii->fMask & MIIM_ID)
4472 lpmii->wID = menu->wID;
4474 if (lpmii->fMask & MIIM_SUBMENU)
4475 lpmii->hSubMenu = menu->hSubMenu;
4477 /* hSubMenu is always cleared
4478 * (not on Win9x/ME ) */
4479 lpmii->hSubMenu = 0;
4482 if (lpmii->fMask & MIIM_CHECKMARKS) {
4483 lpmii->hbmpChecked = menu->hCheckBit;
4484 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4486 if (lpmii->fMask & MIIM_DATA)
4487 lpmii->dwItemData = menu->dwItemData;
4492 /**********************************************************************
4493 * GetMenuItemInfoA (USER32.@)
4495 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4496 LPMENUITEMINFOA lpmii)
4500 if( lpmii->cbSize != sizeof( mii) &&
4501 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4502 SetLastError( ERROR_INVALID_PARAMETER);
4505 memcpy( &mii, lpmii, lpmii->cbSize);
4506 mii.cbSize = sizeof( mii);
4507 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4508 (LPMENUITEMINFOW)&mii, FALSE);
4509 mii.cbSize = lpmii->cbSize;
4510 memcpy( lpmii, &mii, mii.cbSize);
4514 /**********************************************************************
4515 * GetMenuItemInfoW (USER32.@)
4517 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4518 LPMENUITEMINFOW lpmii)
4522 if( lpmii->cbSize != sizeof( mii) &&
4523 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4524 SetLastError( ERROR_INVALID_PARAMETER);
4527 memcpy( &mii, lpmii, lpmii->cbSize);
4528 mii.cbSize = sizeof( mii);
4529 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4530 mii.cbSize = lpmii->cbSize;
4531 memcpy( lpmii, &mii, mii.cbSize);
4536 /* set a menu item text from a ASCII or Unicode string */
4537 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4543 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4544 strcpyW( menu->text, text );
4548 LPCSTR str = (LPCSTR)text;
4549 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4550 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4551 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4556 /**********************************************************************
4557 * SetMenuItemInfo_common
4560 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4561 const MENUITEMINFOW *lpmii,
4564 if (!menu) return FALSE;
4566 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4568 if (lpmii->fMask & MIIM_TYPE ) {
4569 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4570 WARN("invalid combination of fMask bits used\n");
4571 /* this does not happen on Win9x/ME */
4572 SetLastError( ERROR_INVALID_PARAMETER);
4576 /* Remove the old type bits and replace them with the new ones */
4577 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4578 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4580 if (IS_STRING_ITEM(menu->fType)) {
4581 HeapFree(GetProcessHeap(), 0, menu->text);
4582 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4583 } else if( (menu->fType) & MFT_BITMAP)
4584 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4587 if (lpmii->fMask & MIIM_FTYPE ) {
4588 if(( lpmii->fType & MFT_BITMAP)) {
4589 SetLastError( ERROR_INVALID_PARAMETER);
4592 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4593 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4595 if (lpmii->fMask & MIIM_STRING ) {
4596 /* free the string when used */
4597 HeapFree(GetProcessHeap(), 0, menu->text);
4598 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4601 if (lpmii->fMask & MIIM_STATE)
4603 /* Other menu items having MFS_DEFAULT are not converted
4605 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4608 if (lpmii->fMask & MIIM_ID)
4609 menu->wID = lpmii->wID;
4611 if (lpmii->fMask & MIIM_SUBMENU) {
4612 menu->hSubMenu = lpmii->hSubMenu;
4613 if (menu->hSubMenu) {
4614 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4616 subMenu->wFlags |= MF_POPUP;
4617 menu->fType |= MF_POPUP;
4620 SetLastError( ERROR_INVALID_PARAMETER);
4625 menu->fType &= ~MF_POPUP;
4628 if (lpmii->fMask & MIIM_CHECKMARKS)
4630 menu->hCheckBit = lpmii->hbmpChecked;
4631 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4633 if (lpmii->fMask & MIIM_DATA)
4634 menu->dwItemData = lpmii->dwItemData;
4636 if (lpmii->fMask & MIIM_BITMAP)
4637 menu->hbmpItem = lpmii->hbmpItem;
4639 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4640 menu->fType |= MFT_SEPARATOR;
4642 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4646 /**********************************************************************
4647 * SetMenuItemInfoA (USER32.@)
4649 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4650 const MENUITEMINFOA *lpmii)
4654 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4656 if( lpmii->cbSize != sizeof( mii) &&
4657 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4658 SetLastError( ERROR_INVALID_PARAMETER);
4661 memcpy( &mii, lpmii, lpmii->cbSize);
4662 if( lpmii->cbSize != sizeof( mii)) {
4663 mii.cbSize = sizeof( mii);
4664 mii.hbmpItem = NULL;
4666 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4667 (const MENUITEMINFOW *)&mii, FALSE);
4670 /**********************************************************************
4671 * SetMenuItemInfoW (USER32.@)
4673 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4674 const MENUITEMINFOW *lpmii)
4678 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4680 if( lpmii->cbSize != sizeof( mii) &&
4681 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4682 SetLastError( ERROR_INVALID_PARAMETER);
4685 memcpy( &mii, lpmii, lpmii->cbSize);
4686 if( lpmii->cbSize != sizeof( mii)) {
4687 mii.cbSize = sizeof( mii);
4688 mii.hbmpItem = NULL;
4690 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4691 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4694 /**********************************************************************
4695 * SetMenuDefaultItem (USER32.@)
4698 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4704 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4706 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4708 /* reset all default-item flags */
4710 for (i = 0; i < menu->nItems; i++, item++)
4712 item->fState &= ~MFS_DEFAULT;
4715 /* no default item */
4724 if ( uItem >= menu->nItems ) return FALSE;
4725 item[uItem].fState |= MFS_DEFAULT;
4730 for (i = 0; i < menu->nItems; i++, item++)
4732 if (item->wID == uItem)
4734 item->fState |= MFS_DEFAULT;
4743 /**********************************************************************
4744 * GetMenuDefaultItem (USER32.@)
4746 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4752 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4754 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4756 /* find default item */
4760 if (! item) return -1;
4762 while ( !( item->fState & MFS_DEFAULT ) )
4765 if (i >= menu->nItems ) return -1;
4768 /* default: don't return disabled items */
4769 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4771 /* search rekursiv when needed */
4772 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4775 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4776 if ( -1 != ret ) return ret;
4778 /* when item not found in submenu, return the popup item */
4780 return ( bypos ) ? i : item->wID;
4785 /**********************************************************************
4786 * InsertMenuItemA (USER32.@)
4788 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4789 const MENUITEMINFOA *lpmii)
4794 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4796 if( lpmii->cbSize != sizeof( mii) &&
4797 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4798 SetLastError( ERROR_INVALID_PARAMETER);
4801 memcpy( &mii, lpmii, lpmii->cbSize);
4802 if( lpmii->cbSize != sizeof( mii)) {
4803 mii.cbSize = sizeof( mii);
4804 mii.hbmpItem = NULL;
4807 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4808 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4812 /**********************************************************************
4813 * InsertMenuItemW (USER32.@)
4815 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4816 const MENUITEMINFOW *lpmii)
4821 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4823 if( lpmii->cbSize != sizeof( mii) &&
4824 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4825 SetLastError( ERROR_INVALID_PARAMETER);
4828 memcpy( &mii, lpmii, lpmii->cbSize);
4829 if( lpmii->cbSize != sizeof( mii)) {
4830 mii.cbSize = sizeof( mii);
4831 mii.hbmpItem = NULL;
4834 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4835 return SetMenuItemInfo_common(item, &mii, TRUE);
4838 /**********************************************************************
4839 * CheckMenuRadioItem (USER32.@)
4842 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4843 UINT first, UINT last, UINT check,
4848 MENUITEM *mi_first = NULL, *mi_check;
4849 HMENU m_first, m_check;
4851 TRACE("%p: %u-%u, check %u, flags %04x\n", hMenu, first, last, check, bypos);
4853 for (i = first; i <= last; i++)
4860 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4861 if (!mi_first) continue;
4862 mi_check = mi_first;
4868 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4869 if (!mi_check) continue;
4872 if (m_first != m_check) continue;
4873 if (mi_check->fType == MFT_SEPARATOR) continue;
4877 mi_check->fType |= MFT_RADIOCHECK;
4878 mi_check->fState |= MFS_CHECKED;
4883 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4884 mi_check->fState &= ~MFS_CHECKED;
4892 /**********************************************************************
4893 * GetMenuItemRect (USER32.@)
4895 * ATTENTION: Here, the returned values in rect are the screen
4896 * coordinates of the item just like if the menu was
4897 * always on the upper left side of the application.
4900 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4903 POPUPMENU *itemMenu;
4907 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4909 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4910 referenceHwnd = hwnd;
4914 itemMenu = MENU_GetMenu(hMenu);
4915 if (itemMenu == NULL)
4918 if(itemMenu->hWnd == 0)
4920 referenceHwnd = itemMenu->hWnd;
4923 if ((rect == NULL) || (item == NULL))
4928 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4934 /**********************************************************************
4935 * SetMenuInfo (USER32.@)
4938 * MIM_APPLYTOSUBMENUS
4939 * actually use the items to draw the menu
4941 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4945 TRACE("(%p %p)\n", hMenu, lpmi);
4947 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4950 if (lpmi->fMask & MIM_BACKGROUND)
4951 menu->hbrBack = lpmi->hbrBack;
4953 if (lpmi->fMask & MIM_HELPID)
4954 menu->dwContextHelpID = lpmi->dwContextHelpID;
4956 if (lpmi->fMask & MIM_MAXHEIGHT)
4957 menu->cyMax = lpmi->cyMax;
4959 if (lpmi->fMask & MIM_MENUDATA)
4960 menu->dwMenuData = lpmi->dwMenuData;
4962 if (lpmi->fMask & MIM_STYLE)
4964 menu->dwStyle = lpmi->dwStyle;
4965 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4966 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4967 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4968 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4976 /**********************************************************************
4977 * GetMenuInfo (USER32.@)
4983 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4986 TRACE("(%p %p)\n", hMenu, lpmi);
4988 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4991 if (lpmi->fMask & MIM_BACKGROUND)
4992 lpmi->hbrBack = menu->hbrBack;
4994 if (lpmi->fMask & MIM_HELPID)
4995 lpmi->dwContextHelpID = menu->dwContextHelpID;
4997 if (lpmi->fMask & MIM_MAXHEIGHT)
4998 lpmi->cyMax = menu->cyMax;
5000 if (lpmi->fMask & MIM_MENUDATA)
5001 lpmi->dwMenuData = menu->dwMenuData;
5003 if (lpmi->fMask & MIM_STYLE)
5004 lpmi->dwStyle = menu->dwStyle;
5012 /**********************************************************************
5013 * SetMenuContextHelpId (USER32.@)
5015 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5019 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5021 if ((menu = MENU_GetMenu(hMenu)))
5023 menu->dwContextHelpID = dwContextHelpID;
5030 /**********************************************************************
5031 * GetMenuContextHelpId (USER32.@)
5033 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5037 TRACE("(%p)\n", hMenu);
5039 if ((menu = MENU_GetMenu(hMenu)))
5041 return menu->dwContextHelpID;
5046 /**********************************************************************
5047 * MenuItemFromPoint (USER32.@)
5049 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5051 POPUPMENU *menu = MENU_GetMenu(hMenu);
5054 /*FIXME: Do we have to handle hWnd here? */
5055 if (!menu) return -1;
5056 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5061 /**********************************************************************
5062 * translate_accelerator
5064 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5065 BYTE fVirt, WORD key, WORD cmd )
5070 if (wParam != key) return FALSE;
5072 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5073 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5074 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5076 if (message == WM_CHAR || message == WM_SYSCHAR)
5078 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5080 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5086 if(fVirt & FVIRTKEY)
5088 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5089 wParam, 0xff & HIWORD(lParam));
5091 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5092 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5096 if (!(lParam & 0x01000000)) /* no special_key */
5098 if ((fVirt & FALT) && (lParam & 0x20000000))
5099 { /* ^^ ALT pressed */
5100 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5109 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5113 HMENU hMenu, hSubMenu, hSysMenu;
5114 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5116 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5117 hSysMenu = get_win_sys_menu( hWnd );
5119 /* find menu item and ask application to initialize it */
5120 /* 1. in the system menu */
5121 hSubMenu = hSysMenu;
5123 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5127 if (!IsWindowEnabled(hWnd))
5131 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5132 if(hSubMenu != hSysMenu)
5134 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5135 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5136 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5138 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5141 else /* 2. in the window's menu */
5145 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5149 if (!IsWindowEnabled(hWnd))
5153 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5154 if(hSubMenu != hMenu)
5156 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5157 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5158 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5160 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5167 if (uSysStat != (UINT)-1)
5169 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5176 if (uStat != (UINT)-1)
5182 if (uStat & (MF_DISABLED|MF_GRAYED))
5194 if( mesg==WM_COMMAND )
5196 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5197 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5199 else if( mesg==WM_SYSCOMMAND )
5201 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5202 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5206 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5207 * #0: unknown (please report!)
5208 * #1: for WM_KEYUP,WM_SYSKEYUP
5209 * #2: mouse is captured
5210 * #3: window is disabled
5211 * #4: it's a disabled system menu option
5212 * #5: it's a menu option, but window is iconic
5213 * #6: it's a menu option, but disabled
5215 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5217 ERR_(accel)(" unknown reason - please report!\n");
5222 /**********************************************************************
5223 * TranslateAcceleratorA (USER32.@)
5224 * TranslateAccelerator (USER32.@)
5226 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5229 LPACCEL16 lpAccelTbl;
5233 if (!hWnd || !msg) return 0;
5235 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5237 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5241 wParam = msg->wParam;
5243 switch (msg->message)
5252 char ch = LOWORD(wParam);
5254 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5255 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5263 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5264 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5268 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5269 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5271 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5276 /**********************************************************************
5277 * TranslateAcceleratorW (USER32.@)
5279 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5282 LPACCEL16 lpAccelTbl;
5285 if (!hWnd || !msg) return 0;
5287 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5289 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5293 switch (msg->message)
5305 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5306 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5310 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5311 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5313 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);