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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
44 #include "wine/port.h"
53 #include "wine/winbase16.h"
54 #include "wine/winuser16.h"
56 #include "wine/server.h"
57 #include "wine/unicode.h"
60 #include "user_private.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(menu);
64 WINE_DECLARE_DEBUG_CHANNEL(accel);
66 /* internal popup menu window messages */
68 #define MM_SETMENUHANDLE (WM_USER + 0)
69 #define MM_GETMENUHANDLE (WM_USER + 1)
71 /* Menu item structure */
73 /* ----------- MENUITEMINFO Stuff ----------- */
74 UINT fType; /* Item type. */
75 UINT fState; /* Item state. */
76 UINT_PTR wID; /* Item id. */
77 HMENU hSubMenu; /* Pop-up menu. */
78 HBITMAP hCheckBit; /* Bitmap when checked. */
79 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
80 LPWSTR text; /* Item text. */
81 ULONG_PTR dwItemData; /* Application defined. */
82 LPWSTR dwTypeData; /* depends on fMask */
83 HBITMAP hbmpItem; /* bitmap */
84 /* ----------- Wine stuff ----------- */
85 RECT rect; /* Item area (relative to menu window) */
86 UINT xTab; /* X position of text after Tab */
87 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
91 /* Popup menu structure */
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD wMagic; /* Magic number */
95 WORD Width; /* Width of the whole menu */
96 WORD Height; /* Height of the whole menu */
97 UINT nItems; /* Number of items in the menu */
98 HWND hWnd; /* Window containing the menu */
99 MENUITEM *items; /* Array of menu items */
100 UINT FocusedItem; /* Currently focused item */
101 HWND hwndOwner; /* window receiving the messages for ownerdraw */
102 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
103 BOOL bScrolling; /* Scroll arrows are active */
104 UINT nScrollPos; /* Current scroll position */
105 UINT nTotalHeight; /* Total height of menu items inside menu */
106 /* ------------ MENUINFO members ------ */
107 DWORD dwStyle; /* Extended menu style */
108 UINT cyMax; /* max height of the whole menu, 0 is screen height */
109 HBRUSH hbrBack; /* brush for menu background */
110 DWORD dwContextHelpID;
111 DWORD dwMenuData; /* application defined value */
112 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
113 SIZE maxBmpSize; /* Maximum size of the bitmap items */
114 } POPUPMENU, *LPPOPUPMENU;
116 /* internal flags for menu tracking */
118 #define TF_ENDMENU 0x0001
119 #define TF_SUSPENDPOPUP 0x0002
120 #define TF_SKIPREMOVE 0x0004
125 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
126 HMENU hTopMenu; /* initial menu */
127 HWND hOwnerWnd; /* where notifications are sent */
131 #define MENU_MAGIC 0x554d /* 'MU' */
136 /* Internal MENU_TrackMenu() flags */
137 #define TPM_INTERNAL 0xF0000000
138 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
139 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
140 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
142 /* Space between 2 columns */
143 #define MENU_COL_SPACE 4
145 /* top and bottom margins for popup menus */
146 #define MENU_TOP_MARGIN 3
147 #define MENU_BOTTOM_MARGIN 2
149 /* (other menu->FocusedItem values give the position of the focused item) */
150 #define NO_SELECTED_ITEM 0xffff
152 #define MENU_ITEM_TYPE(flags) \
153 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
156 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
157 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159 #define IS_SYSTEM_MENU(menu) \
160 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
165 MF_POPUP | MF_SYSMENU | MF_HELP)
166 #define STATE_MASK (~TYPE_MASK)
168 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
170 static SIZE menucharsize;
171 static UINT ODitemheight; /* default owner drawn item height */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu = FALSE;
180 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
182 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class =
189 POPUPMENU_CLASS_ATOMA, /* name */
190 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
191 NULL, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc, /* procW */
193 sizeof(HMENU), /* extra */
194 IDC_ARROW, /* cursor */
195 (HBRUSH)(COLOR_MENU+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
208 #define MENUOUT(text) \
209 TRACE("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
216 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
219 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
220 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "HBMMENU_MBAR_CLOSE",
221 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
222 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
223 TRACE("%s ", prefix);
225 UINT flags = mp->fType;
226 TRACE( "{ ID=0x%x", mp->wID);
228 TRACE( ", Sub=%p", mp->hSubMenu);
232 MENUFLAG( MFT_SEPARATOR, "sep");
233 MENUFLAG( MFT_OWNERDRAW, "own");
234 MENUFLAG( MFT_BITMAP, "bit");
235 MENUFLAG(MF_POPUP, "pop");
236 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
237 MENUFLAG(MFT_MENUBREAK, "brk");
238 MENUFLAG(MFT_RADIOCHECK, "radio");
239 MENUFLAG(MFT_RIGHTORDER, "rorder");
240 MENUFLAG(MF_SYSMENU, "sys");
241 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
243 TRACE( "+0x%x", flags);
249 MENUFLAG(MFS_GRAYED, "grey");
250 MENUFLAG(MFS_DEFAULT, "default");
251 MENUFLAG(MFS_DISABLED, "dis");
252 MENUFLAG(MFS_CHECKED, "check");
253 MENUFLAG(MFS_HILITE, "hi");
254 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
255 MENUFLAG(MF_MOUSESELECT, "mouse");
257 TRACE( "+0x%x", flags);
260 TRACE( ", Chk=%p", mp->hCheckBit);
262 TRACE( ", Unc=%p", mp->hUnCheckBit);
264 TRACE( ", Text=%s", debugstr_w(mp->text));
266 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
269 if( IS_MAGIC_BITMAP(mp->hbmpItem))
270 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
272 TRACE( ", hbitmap=%p", mp->hbmpItem);
277 TRACE(" %s\n", postfix);
284 /***********************************************************************
287 * Validate the given menu handle and returns the menu structure pointer.
289 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
291 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
292 if (!menu || menu->wMagic != MENU_MAGIC)
294 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
300 /***********************************************************************
303 * Get the system menu of a window
305 static HMENU get_win_sys_menu( HWND hwnd )
308 WND *win = WIN_GetPtr( hwnd );
309 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 WIN_ReleasePtr( win );
317 /***********************************************************************
320 static HFONT get_menu_font( BOOL bold )
322 static HFONT hMenuFont, hMenuFontBold;
324 HFONT ret = bold ? hMenuFontBold : hMenuFont;
328 NONCLIENTMETRICSW ncm;
331 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
332 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
336 ncm.lfMenuFont.lfWeight += 300;
337 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
339 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
340 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344 /* another thread beat us to it */
352 /***********************************************************************
355 static HBITMAP get_arrow_bitmap(void)
357 static HBITMAP arrow_bitmap;
359 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363 /***********************************************************************
364 * get_down_arrow_bitmap
366 static HBITMAP get_down_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374 /***********************************************************************
375 * get_down_arrow_inactive_bitmap
377 static HBITMAP get_down_arrow_inactive_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385 /***********************************************************************
386 * get_up_arrow_bitmap
388 static HBITMAP get_up_arrow_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396 /***********************************************************************
397 * get_up_arrow_inactive_bitmap
399 static HBITMAP get_up_arrow_inactive_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407 /***********************************************************************
410 * Return the default system menu.
412 static HMENU MENU_CopySysPopup(void)
414 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
415 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
418 POPUPMENU* menu = MENU_GetMenu(hMenu);
419 menu->wFlags |= MF_SYSMENU | MF_POPUP;
420 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
423 ERR("Unable to load default system menu\n" );
425 TRACE("returning %p.\n", hMenu );
431 /**********************************************************************
434 * Create a copy of the system menu. System menu in Windows is
435 * a special menu bar with the single entry - system menu popup.
436 * This popup is presented to the outside world as a "system menu".
437 * However, the real system menu handle is sometimes seen in the
438 * WM_MENUSELECT parameters (and Word 6 likes it this way).
440 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
444 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
445 if ((hMenu = CreateMenu()))
447 POPUPMENU *menu = MENU_GetMenu(hMenu);
448 menu->wFlags = MF_SYSMENU;
449 menu->hWnd = WIN_GetFullHandle( hWnd );
450 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
453 hPopupMenu = MENU_CopySysPopup();
457 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
458 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
460 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
461 (UINT_PTR)hPopupMenu, NULL );
463 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
464 menu->items[0].fState = 0;
465 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
467 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
470 DestroyMenu( hMenu );
472 ERR("failed to load system menu!\n");
477 /***********************************************************************
478 * MENU_InitSysMenuPopup
480 * Grey the appropriate items in System menu.
482 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
486 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
487 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
488 gray = ((style & WS_MAXIMIZE) != 0);
489 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
490 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
491 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
492 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
493 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
494 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
495 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
496 gray = (clsStyle & CS_NOCLOSE) != 0;
498 /* The menu item must keep its state if it's disabled */
500 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
504 /******************************************************************************
506 * UINT MENU_GetStartOfNextColumn(
509 *****************************************************************************/
511 static UINT MENU_GetStartOfNextColumn(
514 POPUPMENU *menu = MENU_GetMenu(hMenu);
518 return NO_SELECTED_ITEM;
520 i = menu->FocusedItem + 1;
521 if( i == NO_SELECTED_ITEM )
524 for( ; i < menu->nItems; ++i ) {
525 if (menu->items[i].fType & MF_MENUBARBREAK)
529 return NO_SELECTED_ITEM;
533 /******************************************************************************
535 * UINT MENU_GetStartOfPrevColumn(
538 *****************************************************************************/
540 static UINT MENU_GetStartOfPrevColumn(
543 POPUPMENU *menu = MENU_GetMenu(hMenu);
547 return NO_SELECTED_ITEM;
549 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
550 return NO_SELECTED_ITEM;
552 /* Find the start of the column */
554 for(i = menu->FocusedItem; i != 0 &&
555 !(menu->items[i].fType & MF_MENUBARBREAK);
559 return NO_SELECTED_ITEM;
561 for(--i; i != 0; --i) {
562 if (menu->items[i].fType & MF_MENUBARBREAK)
566 TRACE("ret %d.\n", i );
573 /***********************************************************************
576 * Find a menu item. Return a pointer on the item, and modifies *hmenu
577 * in case the item was in a sub-menu.
579 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
582 MENUITEM *fallback = NULL;
585 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
586 if (wFlags & MF_BYPOSITION)
588 if (*nPos >= menu->nItems) return NULL;
589 return &menu->items[*nPos];
593 MENUITEM *item = menu->items;
594 for (i = 0; i < menu->nItems; i++, item++)
596 if (item->fType & MF_POPUP)
598 HMENU hsubmenu = item->hSubMenu;
599 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
605 if ((UINT_PTR)item->hSubMenu == *nPos)
606 fallback = item; /* fallback to this item if nothing else found */
608 else if (item->wID == *nPos)
618 /***********************************************************************
621 * Find a Sub menu. Return the position of the submenu, and modifies
622 * *hmenu in case it is found in another sub-menu.
623 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
625 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
630 if (((*hmenu)==(HMENU)0xffff) ||
631 (!(menu = MENU_GetMenu(*hmenu))))
632 return NO_SELECTED_ITEM;
634 for (i = 0; i < menu->nItems; i++, item++) {
635 if(!(item->fType & MF_POPUP)) continue;
636 if (item->hSubMenu == hSubTarget) {
640 HMENU hsubmenu = item->hSubMenu;
641 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
642 if (pos != NO_SELECTED_ITEM) {
648 return NO_SELECTED_ITEM;
651 /***********************************************************************
654 static void MENU_FreeItemData( MENUITEM* item )
657 HeapFree( GetProcessHeap(), 0, item->text );
660 /***********************************************************************
661 * MENU_AdjustMenuItemRect
663 * Adjust menu item rectangle according to scrolling state.
666 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
668 if (menu->bScrolling)
670 UINT arrow_bitmap_width, arrow_bitmap_height;
673 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
674 arrow_bitmap_width = bmp.bmWidth;
675 arrow_bitmap_height = bmp.bmHeight;
676 rect->top += arrow_bitmap_height - menu->nScrollPos;
677 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
682 /***********************************************************************
683 * MENU_FindItemByCoords
685 * Find the item at the specified coordinates (screen coords). Does
686 * not work for child windows and therefore should not be called for
687 * an arbitrary system menu.
689 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
690 POINT pt, UINT *pos )
697 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
698 pt.x -= wrect.left;pt.y -= wrect.top;
700 for (i = 0; i < menu->nItems; i++, item++)
703 MENU_AdjustMenuItemRect(menu, &rect);
704 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
705 (pt.y >= rect.top) && (pt.y < rect.bottom))
715 /***********************************************************************
718 * Find the menu item selected by a key press.
719 * Return item id, -1 if none, -2 if we should close the menu.
721 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
722 WCHAR key, BOOL forceMenuChar )
724 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
726 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
730 POPUPMENU *menu = MENU_GetMenu( hmenu );
731 MENUITEM *item = menu->items;
738 for (i = 0; i < menu->nItems; i++, item++)
742 WCHAR *p = item->text - 2;
745 p = strchrW (p + 2, '&');
747 while (p != NULL && p [1] == '&');
748 if (p && (toupperW(p[1]) == toupperW(key))) return i;
752 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
753 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
754 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
755 if (HIWORD(menuchar) == 1) return (UINT)(-2);
761 /***********************************************************************
762 * MENU_GetBitmapItemSize
764 * Get the size of a bitmap item.
766 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
770 HBITMAP bmp = lpitem->hbmpItem;
772 size->cx = size->cy = 0;
774 /* check if there is a magic menu item associated with this item */
775 switch( (INT_PTR) bmp )
777 case (INT_PTR)HBMMENU_CALLBACK:
779 MEASUREITEMSTRUCT measItem;
780 measItem.CtlType = ODT_MENU;
782 measItem.itemID = lpitem->wID;
783 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
784 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
785 measItem.itemData = lpitem->dwItemData;
786 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
787 size->cx = measItem.itemWidth;
788 size->cy = measItem.itemHeight;
792 case (INT_PTR)HBMMENU_SYSTEM:
793 if (lpitem->dwItemData)
795 bmp = (HBITMAP)lpitem->dwItemData;
799 case (INT_PTR)HBMMENU_MBAR_RESTORE:
800 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
801 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
804 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
807 case (INT_PTR)HBMMENU_POPUP_CLOSE:
808 case (INT_PTR)HBMMENU_POPUP_RESTORE:
809 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
810 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
811 FIXME("Magic %p not implemented\n", bmp );
814 if (GetObjectW(bmp, sizeof(bm), &bm ))
816 size->cx = bm.bmWidth;
817 size->cy = bm.bmHeight;
821 /***********************************************************************
822 * MENU_DrawBitmapItem
824 * Draw a bitmap item.
826 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
827 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
833 int w = rect->right - rect->left;
834 int h = rect->bottom - rect->top;
837 HBITMAP hbmToDraw = lpitem->hbmpItem;
840 /* Check if there is a magic menu item associated with this item */
841 if (IS_MAGIC_BITMAP(hbmToDraw))
846 switch((INT_PTR)hbmToDraw)
848 case (INT_PTR)HBMMENU_SYSTEM:
849 if (lpitem->dwItemData)
851 bmp = (HBITMAP)lpitem->dwItemData;
852 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
856 static HBITMAP hBmpSysMenu;
858 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
860 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
861 /* only use right half of the bitmap */
862 bmp_xoffset = bm.bmWidth / 2;
863 bm.bmWidth -= bmp_xoffset;
866 case (INT_PTR)HBMMENU_MBAR_RESTORE:
867 flags = DFCS_CAPTIONRESTORE;
869 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
870 flags = DFCS_CAPTIONMIN;
872 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
873 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
875 case (INT_PTR)HBMMENU_MBAR_CLOSE:
876 flags = DFCS_CAPTIONCLOSE;
878 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
879 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
881 case (INT_PTR)HBMMENU_CALLBACK:
883 DRAWITEMSTRUCT drawItem;
884 drawItem.CtlType = ODT_MENU;
886 drawItem.itemID = lpitem->wID;
887 drawItem.itemAction = odaction;
888 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
889 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
890 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
891 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
892 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
893 drawItem.hwndItem = (HWND)hmenu;
895 drawItem.itemData = lpitem->dwItemData;
896 drawItem.rcItem = *rect;
897 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
901 case (INT_PTR)HBMMENU_POPUP_CLOSE:
902 case (INT_PTR)HBMMENU_POPUP_RESTORE:
903 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
904 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
906 FIXME("Magic %p not implemented\n", hbmToDraw);
910 InflateRect( &r, -1, -1 );
911 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
912 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
916 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
919 hdcMem = CreateCompatibleDC( hdc );
920 SelectObject( hdcMem, bmp );
922 /* handle fontsize > bitmap_height */
923 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
925 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
926 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
927 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
928 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
933 /***********************************************************************
936 * Calculate the size of the menu item and store it in lpitem->rect.
938 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
939 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
942 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
943 UINT arrow_bitmap_width;
947 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
948 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
949 (menuBar ? " (MenuBar)" : ""));
951 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
952 arrow_bitmap_width = bm.bmWidth;
954 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
955 if( !menucharsize.cx ) {
956 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
957 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
958 * but it is unlikely an application will depend on that */
959 ODitemheight = HIWORD( GetDialogBaseUnits());
962 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
964 if (lpitem->fType & MF_OWNERDRAW)
966 MEASUREITEMSTRUCT mis;
967 mis.CtlType = ODT_MENU;
969 mis.itemID = lpitem->wID;
970 mis.itemData = lpitem->dwItemData;
971 mis.itemHeight = ODitemheight;
973 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
974 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
975 * width of a menufont character to the width of an owner-drawn menu.
977 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
979 /* under at least win95 you seem to be given a standard
980 height for the menu and the height value is ignored */
981 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
983 lpitem->rect.bottom += mis.itemHeight;
985 TRACE("id=%04x size=%ldx%ld\n",
986 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
987 lpitem->rect.bottom-lpitem->rect.top);
991 if (lpitem->fType & MF_SEPARATOR)
993 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
995 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1002 if (lpitem->hbmpItem) {
1005 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1006 /* Keep the size of the bitmap in callback mode to be able
1007 * to draw it correctly */
1008 lpitem->bmpsize = size;
1009 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1010 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1011 lpitem->rect.right += size.cx + 2;
1012 itemheight = size.cy + 2;
1014 lpitem->rect.right += 4 + arrow_bitmap_width + menucharsize.cx;
1015 if( !(lppop->dwStyle & MNS_NOCHECK))
1016 lpitem->rect.right += check_bitmap_width;
1017 } else if (lpitem->hbmpItem) { /* menuBar */
1020 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1021 lpitem->bmpsize = size;
1022 lpitem->rect.right += size.cx;
1023 if( lpitem->text) lpitem->rect.right += 2;
1024 itemheight = size.cy;
1027 /* it must be a text item - unless it's the system menu */
1028 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1029 HFONT hfontOld = NULL;
1030 RECT rc = lpitem->rect;
1031 LONG txtheight, txtwidth;
1034 if ( lpitem->fState & MFS_DEFAULT ) {
1035 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1038 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1039 DT_SINGLELINE|DT_CALCRECT);
1040 lpitem->rect.right += rc.right - rc.left;
1041 itemheight = max( max( itemheight, txtheight),
1042 GetSystemMetrics( SM_CYMENU) - 1);
1043 lpitem->rect.right += 2 * menucharsize.cx;
1045 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1048 int n = (int)( p - lpitem->text);
1049 /* Item contains a tab (only meaningful in popup menus) */
1050 /* get text size before the tab */
1051 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1052 DT_SINGLELINE|DT_CALCRECT);
1053 txtwidth = rc.right - rc.left;
1054 p += 1; /* advance past the Tab */
1055 /* get text size after the tab */
1056 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1057 DT_SINGLELINE|DT_CALCRECT);
1058 lpitem->xTab = menucharsize.cx +
1059 4 + check_bitmap_width + lpitem->bmpsize.cx + txtwidth;
1060 txtheight = max( txtheight, tmpheight);
1061 txtwidth += menucharsize.cx + /* space for the tab */
1062 tmprc.right - tmprc.left; /* space for the short cut */
1064 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1065 DT_SINGLELINE|DT_CALCRECT);
1066 txtwidth = rc.right - rc.left;
1067 if (strchrW( lpitem->text, '\b' ))
1068 lpitem->rect.right += menucharsize.cx;
1069 lpitem->xTab = 4 + check_bitmap_width + lpitem->bmpsize.cx +
1072 if( (lppop->dwStyle & MNS_NOCHECK))
1073 lpitem->xTab -= check_bitmap_width;
1074 lpitem->rect.right += 2 + txtwidth;
1075 itemheight = max( itemheight,
1076 max( txtheight + 2, menucharsize.cy + 4));
1078 if (hfontOld) SelectObject (hdc, hfontOld);
1079 } else if( menuBar) {
1080 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1082 lpitem->rect.bottom += itemheight;
1083 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1087 /***********************************************************************
1088 * MENU_GetMaxPopupHeight
1091 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1094 return lppop->cyMax;
1095 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1099 /***********************************************************************
1100 * MENU_PopupMenuCalcSize
1102 * Calculate the size of a popup menu.
1104 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1109 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1111 lppop->Width = lppop->Height = 0;
1112 if (lppop->nItems == 0) return;
1115 SelectObject( hdc, get_menu_font(FALSE));
1120 lppop->maxBmpSize.cx = 0;
1121 lppop->maxBmpSize.cy = 0;
1123 while (start < lppop->nItems)
1125 lpitem = &lppop->items[start];
1127 if( lpitem->fType & MF_MENUBREAK)
1128 orgX += MENU_COL_SPACE;
1129 orgY = MENU_TOP_MARGIN;
1131 maxTab = maxTabWidth = 0;
1132 /* Parse items until column break or end of menu */
1133 for (i = start; i < lppop->nItems; i++, lpitem++)
1136 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1138 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1140 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1141 maxX = max( maxX, lpitem->rect.right );
1142 orgY = lpitem->rect.bottom;
1143 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1145 maxTab = max( maxTab, lpitem->xTab );
1146 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1150 /* Finish the column (set all items to the largest width found) */
1151 maxX = max( maxX, maxTab + maxTabWidth );
1152 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1154 lpitem->rect.right = maxX;
1155 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1156 lpitem->xTab = maxTab;
1159 lppop->Height = max( lppop->Height, orgY );
1162 lppop->Width = maxX;
1164 /* space for 3d border */
1165 lppop->Height += MENU_BOTTOM_MARGIN;
1168 /* Adjust popup height if it exceeds maximum */
1169 maxHeight = MENU_GetMaxPopupHeight(lppop);
1170 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1171 if (lppop->Height >= maxHeight)
1173 lppop->Height = maxHeight;
1174 lppop->bScrolling = TRUE;
1178 lppop->bScrolling = FALSE;
1181 ReleaseDC( 0, hdc );
1185 /***********************************************************************
1186 * MENU_MenuBarCalcSize
1188 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1189 * height is off by 1 pixel which causes lengthy window relocations when
1190 * active document window is maximized/restored.
1192 * Calculate the size of the menu bar.
1194 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1195 LPPOPUPMENU lppop, HWND hwndOwner )
1198 int start, i, orgX, orgY, maxY, helpPos;
1200 if ((lprect == NULL) || (lppop == NULL)) return;
1201 if (lppop->nItems == 0) return;
1202 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1203 lppop->Width = lprect->right - lprect->left;
1205 maxY = lprect->top+1;
1208 lppop->maxBmpSize.cx = 0;
1209 lppop->maxBmpSize.cy = 0;
1210 while (start < lppop->nItems)
1212 lpitem = &lppop->items[start];
1213 orgX = lprect->left;
1216 /* Parse items until line break or end of menu */
1217 for (i = start; i < lppop->nItems; i++, lpitem++)
1219 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1221 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1223 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1224 debug_print_menuitem (" item: ", lpitem, "");
1225 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1227 if (lpitem->rect.right > lprect->right)
1229 if (i != start) break;
1230 else lpitem->rect.right = lprect->right;
1232 maxY = max( maxY, lpitem->rect.bottom );
1233 orgX = lpitem->rect.right;
1236 /* Finish the line (set all items to the largest height found) */
1237 while (start < i) lppop->items[start++].rect.bottom = maxY;
1240 lprect->bottom = maxY;
1241 lppop->Height = lprect->bottom - lprect->top;
1243 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1244 /* the last item (if several lines, only move the last line) */
1245 lpitem = &lppop->items[lppop->nItems-1];
1246 orgY = lpitem->rect.top;
1247 orgX = lprect->right;
1248 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1249 if ( (helpPos==-1) || (helpPos>i) )
1251 if (lpitem->rect.top != orgY) break; /* Other line */
1252 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1253 lpitem->rect.left += orgX - lpitem->rect.right;
1254 lpitem->rect.right = orgX;
1255 orgX = lpitem->rect.left;
1260 /***********************************************************************
1261 * MENU_DrawScrollArrows
1263 * Draw scroll arrows.
1266 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1268 HDC hdcMem = CreateCompatibleDC(hdc);
1269 HBITMAP hOrigBitmap;
1270 UINT arrow_bitmap_width, arrow_bitmap_height;
1274 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1275 arrow_bitmap_width = bmp.bmWidth;
1276 arrow_bitmap_height = bmp.bmHeight;
1279 if (lppop->nScrollPos)
1280 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1282 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1285 rect.right = lppop->Width;
1286 rect.bottom = arrow_bitmap_height;
1287 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1288 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1289 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1290 rect.top = lppop->Height - arrow_bitmap_height;
1291 rect.bottom = lppop->Height;
1292 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1294 SelectObject(hdcMem, get_down_arrow_bitmap());
1296 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1297 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1298 lppop->Height - arrow_bitmap_height,
1299 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1300 SelectObject(hdcMem, hOrigBitmap);
1305 /***********************************************************************
1308 * Draws the popup-menu arrow.
1310 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1311 UINT arrow_bitmap_height)
1313 HDC hdcMem = CreateCompatibleDC( hdc );
1314 HBITMAP hOrigBitmap;
1316 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1317 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1318 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1319 arrow_bitmap_width, arrow_bitmap_height,
1320 hdcMem, 0, 0, SRCCOPY );
1321 SelectObject( hdcMem, hOrigBitmap );
1324 /***********************************************************************
1327 * Draw a single menu item.
1329 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1330 UINT height, BOOL menuBar, UINT odaction )
1333 BOOL flat_menu = FALSE;
1335 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1336 POPUPMENU *menu = MENU_GetMenu(hmenu);
1339 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1343 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1344 arrow_bitmap_width = bmp.bmWidth;
1345 arrow_bitmap_height = bmp.bmHeight;
1348 if (lpitem->fType & MF_SYSMENU)
1350 if( !IsIconic(hwnd) )
1351 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1355 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1356 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1360 if (lpitem->fState & MF_HILITE)
1362 if(menuBar && !flat_menu) {
1363 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1364 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1366 if(lpitem->fState & MF_GRAYED)
1367 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1369 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1370 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1375 if (lpitem->fState & MF_GRAYED)
1376 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1378 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1379 SetBkColor( hdc, GetSysColor( bkgnd ) );
1382 if (lpitem->fType & MF_OWNERDRAW)
1385 ** Experimentation under Windows reveals that an owner-drawn
1386 ** menu is given the rectangle which includes the space it requested
1387 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1388 ** and a popup-menu arrow. This is the value of lpitem->rect.
1389 ** Windows will leave all drawing to the application except for
1390 ** the popup-menu arrow. Windows always draws that itself, after
1391 ** the menu owner has finished drawing.
1395 dis.CtlType = ODT_MENU;
1397 dis.itemID = lpitem->wID;
1398 dis.itemData = lpitem->dwItemData;
1400 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1401 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1402 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1403 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1404 dis.hwndItem = (HWND)hmenu;
1406 dis.rcItem = lpitem->rect;
1407 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &dis.rcItem);
1408 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1409 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1410 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1411 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1412 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1413 /* Draw the popup-menu arrow */
1414 if (lpitem->fType & MF_POPUP)
1415 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1416 arrow_bitmap_height);
1420 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1422 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1424 rect = lpitem->rect;
1425 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1427 if (lpitem->fState & MF_HILITE)
1431 InflateRect (&rect, -1, -1);
1432 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1433 InflateRect (&rect, 1, 1);
1434 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1439 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1441 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1445 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1447 SetBkMode( hdc, TRANSPARENT );
1449 /* vertical separator */
1450 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1456 rc.bottom = height - 3;
1459 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1460 MoveToEx( hdc, rc.left, rc.top, NULL );
1461 LineTo( hdc, rc.left, rc.bottom );
1462 SelectObject( hdc, oldPen );
1465 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1468 /* horizontal separator */
1469 if (lpitem->fType & MF_SEPARATOR)
1476 rc.top = ( rc.top + rc.bottom) / 2;
1479 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1480 MoveToEx( hdc, rc.left, rc.top, NULL );
1481 LineTo( hdc, rc.right, rc.top );
1482 SelectObject( hdc, oldPen );
1485 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 /* helper lines for debugging */
1490 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1491 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1492 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1493 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1496 if (lpitem->hbmpItem) {
1497 /* calculate the bitmap rectangle in coordinates relative
1498 * to the item rectangle */
1500 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1503 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1506 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1507 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1509 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1510 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1513 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1514 lpitem->bmpsize.cy) / 2;
1515 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1521 INT y = rect.top + rect.bottom;
1523 int checked = FALSE;
1524 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1525 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1526 /* Draw the check mark
1529 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1531 if( !(menu->dwStyle & MNS_NOCHECK)) {
1532 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1533 lpitem->hUnCheckBit;
1534 if (bm) /* we have a custom bitmap */
1536 HDC hdcMem = CreateCompatibleDC( hdc );
1538 SelectObject( hdcMem, bm );
1539 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1540 check_bitmap_width, check_bitmap_height,
1541 hdcMem, 0, 0, SRCCOPY );
1545 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1548 HBITMAP bm = CreateBitmap( check_bitmap_width,
1549 check_bitmap_height, 1, 1, NULL );
1550 HDC hdcMem = CreateCompatibleDC( hdc );
1552 SelectObject( hdcMem, bm );
1553 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1554 DrawFrameControl( hdcMem, &r, DFC_MENU,
1555 (lpitem->fType & MFT_RADIOCHECK) ?
1556 DFCS_MENUBULLET : DFCS_MENUCHECK );
1557 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1558 hdcMem, 0, 0, SRCCOPY );
1564 if( lpitem->hbmpItem &&
1565 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1567 /* some applications make this assumption on the DC's origin */
1568 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1569 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1571 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1573 /* Draw the popup-menu arrow */
1574 if (lpitem->fType & MF_POPUP)
1575 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1576 arrow_bitmap_height);
1578 if( !(menu->dwStyle & MNS_NOCHECK))
1579 rect.left += check_bitmap_width;
1580 rect.right -= arrow_bitmap_width;
1582 else if( lpitem->hbmpItem)
1583 { /* Draw the bitmap */
1586 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1587 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1589 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1591 /* process text if present */
1597 UINT uFormat = (menuBar) ?
1598 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1599 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1601 if( !(menu->dwStyle & MNS_CHECKORBMP))
1602 rect.left += menu->maxBmpSize.cx;
1604 if ( lpitem->fState & MFS_DEFAULT )
1606 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1610 if( lpitem->hbmpItem)
1611 rect.left += lpitem->bmpsize.cx;
1612 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1613 rect.left += menucharsize.cx;
1614 rect.right -= menucharsize.cx;
1617 for (i = 0; lpitem->text[i]; i++)
1618 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1621 if(lpitem->fState & MF_GRAYED)
1623 if (!(lpitem->fState & MF_HILITE) )
1625 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1626 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1627 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1628 --rect.left; --rect.top; --rect.right; --rect.bottom;
1630 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1633 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1635 /* paint the shortcut text */
1636 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1638 if (lpitem->text[i] == '\t')
1640 rect.left = lpitem->xTab;
1641 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1645 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1648 if(lpitem->fState & MF_GRAYED)
1650 if (!(lpitem->fState & MF_HILITE) )
1652 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1653 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1654 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1655 --rect.left; --rect.top; --rect.right; --rect.bottom;
1657 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1659 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1663 SelectObject (hdc, hfontOld);
1668 /***********************************************************************
1669 * MENU_DrawPopupMenu
1671 * Paint a popup menu.
1673 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1675 HBRUSH hPrevBrush = 0;
1678 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1680 GetClientRect( hwnd, &rect );
1682 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1683 && (SelectObject( hdc, get_menu_font(FALSE))))
1687 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1689 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1693 BOOL flat_menu = FALSE;
1695 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1697 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1699 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1701 menu = MENU_GetMenu( hmenu );
1703 /* draw menu items */
1704 if (menu && menu->nItems)
1709 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1710 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1711 menu->Height, FALSE, ODA_DRAWENTIRE );
1715 /* draw scroll arrows */
1716 if (menu->bScrolling)
1717 MENU_DrawScrollArrows(menu, hdc);
1720 SelectObject( hdc, hPrevBrush );
1725 /***********************************************************************
1728 * Paint a menu bar. Returns the height of the menu bar.
1729 * called from [windows/nonclient.c]
1731 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1736 HMENU hMenu = GetMenu(hwnd);
1738 lppop = MENU_GetMenu( hMenu );
1739 if (lppop == NULL || lprect == NULL)
1741 return GetSystemMetrics(SM_CYMENU);
1746 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1748 if (lppop->Height == 0)
1749 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1751 lprect->bottom = lprect->top + lppop->Height;
1753 if (hfontOld) SelectObject( hDC, hfontOld);
1754 return lppop->Height;
1757 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1761 /***********************************************************************
1764 * Display a popup menu.
1766 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1767 INT x, INT y, INT xanchor, INT yanchor )
1772 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1773 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1775 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1776 if (menu->FocusedItem != NO_SELECTED_ITEM)
1778 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1779 menu->FocusedItem = NO_SELECTED_ITEM;
1782 /* store the owner for DrawItem */
1783 menu->hwndOwner = hwndOwner;
1785 menu->nScrollPos = 0;
1786 MENU_PopupMenuCalcSize( menu, hwndOwner );
1788 /* adjust popup menu pos so that it fits within the desktop */
1790 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1791 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1793 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1796 x -= width - xanchor;
1797 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1798 x = GetSystemMetrics(SM_CXSCREEN) - width;
1802 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1805 y -= height + yanchor;
1806 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1807 y = GetSystemMetrics(SM_CYSCREEN) - height;
1811 /* NOTE: In Windows, top menu popup is not owned. */
1812 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1813 WS_POPUP, x, y, width, height,
1814 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1816 if( !menu->hWnd ) return FALSE;
1817 if (!top_popup) top_popup = menu->hWnd;
1819 /* Display the window */
1821 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1822 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1823 UpdateWindow( menu->hWnd );
1828 /***********************************************************************
1829 * MENU_EnsureMenuItemVisible
1832 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1834 if (lppop->bScrolling)
1836 MENUITEM *item = &lppop->items[wIndex];
1837 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1838 UINT nOldPos = lppop->nScrollPos;
1840 UINT arrow_bitmap_height;
1843 GetClientRect(lppop->hWnd, &rc);
1845 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1846 arrow_bitmap_height = bmp.bmHeight;
1848 rc.top += arrow_bitmap_height;
1849 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1851 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1852 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1855 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1856 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1857 MENU_DrawScrollArrows(lppop, hdc);
1859 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1861 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1862 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1863 MENU_DrawScrollArrows(lppop, hdc);
1869 /***********************************************************************
1872 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1873 BOOL sendMenuSelect, HMENU topmenu )
1878 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1880 lppop = MENU_GetMenu( hmenu );
1881 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1883 if (lppop->FocusedItem == wIndex) return;
1884 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1885 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1886 if (!top_popup) top_popup = lppop->hWnd;
1888 SelectObject( hdc, get_menu_font(FALSE));
1890 /* Clear previous highlighted item */
1891 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1893 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1894 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1895 lppop->Height, !(lppop->wFlags & MF_POPUP),
1899 /* Highlight new item (if any) */
1900 lppop->FocusedItem = wIndex;
1901 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1903 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1904 lppop->items[wIndex].fState |= MF_HILITE;
1905 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1906 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1907 &lppop->items[wIndex], lppop->Height,
1908 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1912 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1913 SendMessageW( hwndOwner, WM_MENUSELECT,
1914 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1915 ip->fType | ip->fState |
1916 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1919 else if (sendMenuSelect) {
1922 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1923 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1924 MENUITEM *ip = &ptm->items[pos];
1925 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1926 ip->fType | ip->fState |
1927 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1931 ReleaseDC( lppop->hWnd, hdc );
1935 /***********************************************************************
1936 * MENU_MoveSelection
1938 * Moves currently selected item according to the offset parameter.
1939 * If there is no selection then it should select the last item if
1940 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1942 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1947 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1949 menu = MENU_GetMenu( hmenu );
1950 if ((!menu) || (!menu->items)) return;
1952 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1954 if( menu->nItems == 1 ) return; else
1955 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1957 if (!(menu->items[i].fType & MF_SEPARATOR))
1959 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1964 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1965 i >= 0 && i < menu->nItems ; i += offset)
1966 if (!(menu->items[i].fType & MF_SEPARATOR))
1968 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1974 /**********************************************************************
1977 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1980 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1983 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1984 TRACE("flags=%x str=%p\n", flags, str);
1986 if (IS_STRING_ITEM(flags))
1988 LPWSTR prevText = item->text;
1991 flags |= MF_SEPARATOR;
1997 /* Item beginning with a backspace is a help item */
2003 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2005 strcpyW( text, str );
2008 item->hbmpItem = NULL;
2009 HeapFree( GetProcessHeap(), 0, prevText );
2011 else if(( flags & MFT_BITMAP)) {
2012 item->hbmpItem = HBITMAP_32(LOWORD(str));
2013 /* setting bitmap clears text */
2014 HeapFree( GetProcessHeap(), 0, item->text );
2018 if (flags & MF_OWNERDRAW)
2019 item->dwItemData = (DWORD_PTR)str;
2021 item->dwItemData = 0;
2023 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2024 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2026 if (flags & MF_POPUP)
2028 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2029 if (menu) menu->wFlags |= MF_POPUP;
2041 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2043 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2044 flags |= MF_POPUP; /* keep popup */
2046 item->fType = flags & TYPE_MASK;
2047 item->fState = (flags & STATE_MASK) &
2048 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2050 /* Don't call SetRectEmpty here! */
2052 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2057 /**********************************************************************
2060 * Insert (allocate) a new item into a menu.
2062 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2067 if (!(menu = MENU_GetMenu(hMenu)))
2070 /* Find where to insert new item */
2072 if (flags & MF_BYPOSITION) {
2073 if (pos > menu->nItems)
2076 if (!MENU_FindItem( &hMenu, &pos, flags ))
2079 if (!(menu = MENU_GetMenu( hMenu )))
2084 /* Create new items array */
2086 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2089 WARN("allocation failed\n" );
2092 if (menu->nItems > 0)
2094 /* Copy the old array into the new one */
2095 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2096 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2097 (menu->nItems-pos)*sizeof(MENUITEM) );
2098 HeapFree( GetProcessHeap(), 0, menu->items );
2100 menu->items = newItems;
2102 memset( &newItems[pos], 0, sizeof(*newItems) );
2103 menu->Height = 0; /* force size recalculate */
2104 return &newItems[pos];
2108 /**********************************************************************
2109 * MENU_ParseResource
2111 * Parse a standard menu resource and add items to the menu.
2112 * Return a pointer to the end of the resource.
2114 * NOTE: flags is equivalent to the mtOption field
2116 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2123 flags = GET_WORD(res);
2124 res += sizeof(WORD);
2125 if (!(flags & MF_POPUP))
2128 res += sizeof(WORD);
2131 if (!unicode) res += strlen(str) + 1;
2132 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2133 if (flags & MF_POPUP)
2135 HMENU hSubMenu = CreatePopupMenu();
2136 if (!hSubMenu) return NULL;
2137 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2139 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2140 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2142 else /* Not a popup */
2144 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2145 else AppendMenuW( hMenu, flags, id,
2146 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2148 } while (!(flags & MF_END));
2153 /**********************************************************************
2154 * MENUEX_ParseResource
2156 * Parse an extended menu resource and add items to the menu.
2157 * Return a pointer to the end of the resource.
2159 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2165 mii.cbSize = sizeof(mii);
2166 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2167 mii.fType = GET_DWORD(res);
2168 res += sizeof(DWORD);
2169 mii.fState = GET_DWORD(res);
2170 res += sizeof(DWORD);
2171 mii.wID = GET_DWORD(res);
2172 res += sizeof(DWORD);
2173 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2174 res += sizeof(WORD);
2175 /* Align the text on a word boundary. */
2176 res += (~((UINT_PTR)res - 1)) & 1;
2177 mii.dwTypeData = (LPWSTR) res;
2178 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2179 /* Align the following fields on a dword boundary. */
2180 res += (~((UINT_PTR)res - 1)) & 3;
2182 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2183 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2185 if (resinfo & 1) { /* Pop-up? */
2186 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2187 res += sizeof(DWORD);
2188 mii.hSubMenu = CreatePopupMenu();
2191 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2192 DestroyMenu(mii.hSubMenu);
2195 mii.fMask |= MIIM_SUBMENU;
2196 mii.fType |= MF_POPUP;
2198 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2200 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2201 mii.wID, mii.fType);
2202 mii.fType |= MF_SEPARATOR;
2204 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2205 } while (!(resinfo & MF_END));
2210 /***********************************************************************
2213 * Return the handle of the selected sub-popup menu (if any).
2215 static HMENU MENU_GetSubPopup( HMENU hmenu )
2220 menu = MENU_GetMenu( hmenu );
2222 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2224 item = &menu->items[menu->FocusedItem];
2225 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2226 return item->hSubMenu;
2231 /***********************************************************************
2232 * MENU_HideSubPopups
2234 * Hide the sub-popup menus of this menu.
2236 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2237 BOOL sendMenuSelect )
2239 POPUPMENU *menu = MENU_GetMenu( hmenu );
2241 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2243 if (menu && top_popup)
2249 if (menu->FocusedItem != NO_SELECTED_ITEM)
2251 item = &menu->items[menu->FocusedItem];
2252 if (!(item->fType & MF_POPUP) ||
2253 !(item->fState & MF_MOUSESELECT)) return;
2254 item->fState &= ~MF_MOUSESELECT;
2255 hsubmenu = item->hSubMenu;
2258 submenu = MENU_GetMenu( hsubmenu );
2259 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2260 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2261 DestroyWindow( submenu->hWnd );
2267 /***********************************************************************
2270 * Display the sub-menu of the selected item of this menu.
2271 * Return the handle of the submenu, or hmenu if no submenu to display.
2273 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2274 BOOL selectFirst, UINT wFlags )
2281 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2283 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2285 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2287 item = &menu->items[menu->FocusedItem];
2288 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2291 /* message must be sent before using item,
2292 because nearly everything may be changed by the application ! */
2294 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2295 if (!(wFlags & TPM_NONOTIFY))
2296 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2297 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2299 item = &menu->items[menu->FocusedItem];
2302 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2303 if (!(item->fState & MF_HILITE))
2305 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2306 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2308 SelectObject( hdc, get_menu_font(FALSE));
2310 item->fState |= MF_HILITE;
2311 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2312 ReleaseDC( menu->hWnd, hdc );
2314 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2317 item->fState |= MF_MOUSESELECT;
2319 if (IS_SYSTEM_MENU(menu))
2321 MENU_InitSysMenuPopup(item->hSubMenu,
2322 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2323 GetClassLongW( menu->hWnd, GCL_STYLE));
2325 NC_GetSysPopupPos( menu->hWnd, &rect );
2326 rect.top = rect.bottom;
2327 rect.right = GetSystemMetrics(SM_CXSIZE);
2328 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2332 GetWindowRect( menu->hWnd, &rect );
2333 if (menu->wFlags & MF_POPUP)
2335 RECT rc = item->rect;
2337 MENU_AdjustMenuItemRect(menu, &rc);
2338 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2340 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2341 rect.bottom = rc.top - rc.bottom;
2345 rect.left += item->rect.left;
2346 rect.top += item->rect.bottom;
2347 rect.right = item->rect.right - item->rect.left;
2348 rect.bottom = item->rect.bottom - item->rect.top;
2352 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2353 rect.left, rect.top, rect.right, rect.bottom );
2355 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2356 return item->hSubMenu;
2361 /**********************************************************************
2364 HWND MENU_IsMenuActive(void)
2369 /***********************************************************************
2372 * Walks menu chain trying to find a menu pt maps to.
2374 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2376 POPUPMENU *menu = MENU_GetMenu( hMenu );
2377 UINT item = menu->FocusedItem;
2380 /* try subpopup first (if any) */
2381 ret = (item != NO_SELECTED_ITEM &&
2382 (menu->items[item].fType & MF_POPUP) &&
2383 (menu->items[item].fState & MF_MOUSESELECT))
2384 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2386 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2388 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2389 if( menu->wFlags & MF_POPUP )
2391 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2393 else if (ht == HTSYSMENU)
2394 ret = get_win_sys_menu( menu->hWnd );
2395 else if (ht == HTMENU)
2396 ret = GetMenu( menu->hWnd );
2401 /***********************************************************************
2402 * MENU_ExecFocusedItem
2404 * Execute a menu item (for instance when user pressed Enter).
2405 * Return the wID of the executed item. Otherwise, -1 indicating
2406 * that no menu item was executed;
2407 * Have to receive the flags for the TrackPopupMenu options to avoid
2408 * sending unwanted message.
2411 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2414 POPUPMENU *menu = MENU_GetMenu( hMenu );
2416 TRACE("%p hmenu=%p\n", pmt, hMenu);
2418 if (!menu || !menu->nItems ||
2419 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2421 item = &menu->items[menu->FocusedItem];
2423 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2425 if (!(item->fType & MF_POPUP))
2427 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2429 /* If TPM_RETURNCMD is set you return the id, but
2430 do not send a message to the owner */
2431 if(!(wFlags & TPM_RETURNCMD))
2433 if( menu->wFlags & MF_SYSMENU )
2434 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2435 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2437 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2443 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2448 /***********************************************************************
2449 * MENU_SwitchTracking
2451 * Helper function for menu navigation routines.
2453 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2455 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2456 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2458 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2460 if( pmt->hTopMenu != hPtMenu &&
2461 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2463 /* both are top level menus (system and menu-bar) */
2464 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2465 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2466 pmt->hTopMenu = hPtMenu;
2468 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2469 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2473 /***********************************************************************
2476 * Return TRUE if we can go on with menu tracking.
2478 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2480 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2485 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2488 if( IS_SYSTEM_MENU(ptmenu) )
2489 item = ptmenu->items;
2491 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2495 if( ptmenu->FocusedItem != id )
2496 MENU_SwitchTracking( pmt, hPtMenu, id );
2498 /* If the popup menu is not already "popped" */
2499 if(!(item->fState & MF_MOUSESELECT ))
2501 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2506 /* Else the click was on the menu bar, finish the tracking */
2511 /***********************************************************************
2514 * Return the value of MENU_ExecFocusedItem if
2515 * the selected item was not a popup. Else open the popup.
2516 * A -1 return value indicates that we go on with menu tracking.
2519 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2521 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2526 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2529 if( IS_SYSTEM_MENU(ptmenu) )
2530 item = ptmenu->items;
2532 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2534 if( item && (ptmenu->FocusedItem == id ))
2536 if( !(item->fType & MF_POPUP) )
2537 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2539 /* If we are dealing with the top-level menu */
2540 /* and this is a click on an already "popped" item: */
2541 /* Stop the menu tracking and close the opened submenus */
2542 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2545 ptmenu->bTimeToHide = TRUE;
2551 /***********************************************************************
2554 * Return TRUE if we can go on with menu tracking.
2556 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2558 UINT id = NO_SELECTED_ITEM;
2559 POPUPMENU *ptmenu = NULL;
2563 ptmenu = MENU_GetMenu( hPtMenu );
2564 if( IS_SYSTEM_MENU(ptmenu) )
2567 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2570 if( id == NO_SELECTED_ITEM )
2572 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2573 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2576 else if( ptmenu->FocusedItem != id )
2578 MENU_SwitchTracking( pmt, hPtMenu, id );
2579 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2585 /***********************************************************************
2588 static void MENU_SetCapture( HWND hwnd )
2592 SERVER_START_REQ( set_capture_window )
2595 req->flags = CAPTURE_MENU;
2596 if (!wine_server_call_err( req ))
2598 previous = reply->previous;
2599 hwnd = reply->full_handle;
2604 if (previous && previous != hwnd)
2605 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2609 /***********************************************************************
2612 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2614 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2616 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2618 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2619 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2621 MDINEXTMENU next_menu;
2626 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2627 next_menu.hmenuNext = 0;
2628 next_menu.hwndNext = 0;
2629 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2631 TRACE("%p [%p] -> %p [%p]\n",
2632 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2634 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2636 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2637 hNewWnd = pmt->hOwnerWnd;
2638 if( IS_SYSTEM_MENU(menu) )
2640 /* switch to the menu bar */
2642 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2646 menu = MENU_GetMenu( hNewMenu );
2647 id = menu->nItems - 1;
2650 else if (style & WS_SYSMENU )
2652 /* switch to the system menu */
2653 hNewMenu = get_win_sys_menu( hNewWnd );
2657 else /* application returned a new menu to switch to */
2659 hNewMenu = next_menu.hmenuNext;
2660 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2662 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2664 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2666 if (style & WS_SYSMENU &&
2667 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2669 /* get the real system menu */
2670 hNewMenu = get_win_sys_menu(hNewWnd);
2672 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2674 /* FIXME: Not sure what to do here;
2675 * perhaps try to track hNewMenu as a popup? */
2677 TRACE(" -- got confused.\n");
2684 if( hNewMenu != pmt->hTopMenu )
2686 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2688 if( pmt->hCurrentMenu != pmt->hTopMenu )
2689 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2692 if( hNewWnd != pmt->hOwnerWnd )
2694 pmt->hOwnerWnd = hNewWnd;
2695 MENU_SetCapture( pmt->hOwnerWnd );
2698 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2699 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2706 /***********************************************************************
2709 * The idea is not to show the popup if the next input message is
2710 * going to hide it anyway.
2712 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2716 msg.hwnd = pmt->hOwnerWnd;
2718 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2719 pmt->trackFlags |= TF_SKIPREMOVE;
2724 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2725 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2727 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2728 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2729 if( msg.message == WM_KEYDOWN &&
2730 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2732 pmt->trackFlags |= TF_SUSPENDPOPUP;
2739 /* failures go through this */
2740 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2744 /***********************************************************************
2747 * Handle a VK_ESCAPE key event in a menu.
2749 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2751 BOOL bEndMenu = TRUE;
2753 if (pmt->hCurrentMenu != pmt->hTopMenu)
2755 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2757 if (menu->wFlags & MF_POPUP)
2759 HMENU hmenutmp, hmenuprev;
2761 hmenuprev = hmenutmp = pmt->hTopMenu;
2763 /* close topmost popup */
2764 while (hmenutmp != pmt->hCurrentMenu)
2766 hmenuprev = hmenutmp;
2767 hmenutmp = MENU_GetSubPopup( hmenuprev );
2770 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2771 pmt->hCurrentMenu = hmenuprev;
2779 /***********************************************************************
2782 * Handle a VK_LEFT key event in a menu.
2784 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2787 HMENU hmenutmp, hmenuprev;
2790 hmenuprev = hmenutmp = pmt->hTopMenu;
2791 menu = MENU_GetMenu( hmenutmp );
2793 /* Try to move 1 column left (if possible) */
2794 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2795 NO_SELECTED_ITEM ) {
2797 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2802 /* close topmost popup */
2803 while (hmenutmp != pmt->hCurrentMenu)
2805 hmenuprev = hmenutmp;
2806 hmenutmp = MENU_GetSubPopup( hmenuprev );
2809 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2810 pmt->hCurrentMenu = hmenuprev;
2812 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2814 /* move menu bar selection if no more popups are left */
2816 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2817 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2819 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2821 /* A sublevel menu was displayed - display the next one
2822 * unless there is another displacement coming up */
2824 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2825 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2826 pmt->hTopMenu, TRUE, wFlags);
2832 /***********************************************************************
2835 * Handle a VK_RIGHT key event in a menu.
2837 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2840 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2843 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2845 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2846 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2848 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2850 /* If already displaying a popup, try to display sub-popup */
2852 hmenutmp = pmt->hCurrentMenu;
2853 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2855 /* if subpopup was displayed then we are done */
2856 if (hmenutmp != pmt->hCurrentMenu) return;
2859 /* Check to see if there's another column */
2860 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2861 NO_SELECTED_ITEM ) {
2862 TRACE("Going to %d.\n", nextcol );
2863 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2868 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2870 if( pmt->hCurrentMenu != pmt->hTopMenu )
2872 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2873 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2874 } else hmenutmp = 0;
2876 /* try to move to the next item */
2877 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2878 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2880 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2881 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2882 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2883 pmt->hTopMenu, TRUE, wFlags);
2887 /***********************************************************************
2890 * Menu tracking code.
2892 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2893 HWND hwnd, const RECT *lprect )
2898 INT executedMenuId = -1;
2900 BOOL enterIdleSent = FALSE;
2903 mt.hCurrentMenu = hmenu;
2904 mt.hTopMenu = hmenu;
2905 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2909 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2910 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2913 if (!(menu = MENU_GetMenu( hmenu )))
2915 WARN("Invalid menu handle %p\n", hmenu);
2916 SetLastError(ERROR_INVALID_MENU_HANDLE);
2920 if (wFlags & TPM_BUTTONDOWN)
2922 /* Get the result in order to start the tracking or not */
2923 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2924 fEndMenu = !fRemove;
2927 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2929 MENU_SetCapture( mt.hOwnerWnd );
2933 menu = MENU_GetMenu( mt.hCurrentMenu );
2934 if (!menu) /* sometimes happens if I do a window manager close */
2937 /* we have to keep the message in the queue until it's
2938 * clear that menu loop is not over yet. */
2942 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2944 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2945 /* remove the message from the queue */
2946 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2952 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2953 enterIdleSent = TRUE;
2954 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2960 /* check if EndMenu() tried to cancel us, by posting this message */
2961 if(msg.message == WM_CANCELMODE)
2963 /* we are now out of the loop */
2966 /* remove the message from the queue */
2967 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2969 /* break out of internal loop, ala ESCAPE */
2973 TranslateMessage( &msg );
2976 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2977 enterIdleSent=FALSE;
2980 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2983 * Use the mouse coordinates in lParam instead of those in the MSG
2984 * struct to properly handle synthetic messages. They are already
2985 * in screen coordinates.
2987 mt.pt.x = (short)LOWORD(msg.lParam);
2988 mt.pt.y = (short)HIWORD(msg.lParam);
2990 /* Find a menu for this mouse event */
2991 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2995 /* no WM_NC... messages in captured state */
2997 case WM_RBUTTONDBLCLK:
2998 case WM_RBUTTONDOWN:
2999 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3001 case WM_LBUTTONDBLCLK:
3002 case WM_LBUTTONDOWN:
3003 /* If the message belongs to the menu, removes it from the queue */
3004 /* Else, end menu tracking */
3005 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3006 fEndMenu = !fRemove;
3010 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3013 /* Check if a menu was selected by the mouse */
3016 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3018 /* End the loop if executedMenuId is an item ID */
3019 /* or if the job was done (executedMenuId = 0). */
3020 fEndMenu = fRemove = (executedMenuId != -1);
3022 /* No menu was selected by the mouse */
3023 /* if the function was called by TrackPopupMenu, continue
3024 with the menu tracking. If not, stop it */
3026 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3031 /* the selected menu item must be changed every time */
3032 /* the mouse moves. */
3035 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3037 } /* switch(msg.message) - mouse */
3039 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3041 fRemove = TRUE; /* Keyboard messages are always removed */
3054 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3055 NO_SELECTED_ITEM, FALSE, 0 );
3058 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3059 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3062 case VK_DOWN: /* If on menu bar, pull-down the menu */
3064 menu = MENU_GetMenu( mt.hCurrentMenu );
3065 if (!(menu->wFlags & MF_POPUP))
3066 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3067 else /* otherwise try to move selection */
3068 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
3072 MENU_KeyLeft( &mt, wFlags );
3076 MENU_KeyRight( &mt, wFlags );
3080 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3086 hi.cbSize = sizeof(HELPINFO);
3087 hi.iContextType = HELPINFO_MENUITEM;
3088 if (menu->FocusedItem == NO_SELECTED_ITEM)
3091 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3092 hi.hItemHandle = hmenu;
3093 hi.dwContextId = menu->dwContextHelpID;
3094 hi.MousePos = msg.pt;
3095 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3102 break; /* WM_KEYDOWN */
3109 if (msg.wParam == '\r' || msg.wParam == ' ')
3111 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3112 fEndMenu = (executedMenuId != -1);
3117 /* Hack to avoid control chars. */
3118 /* We will find a better way real soon... */
3119 if (msg.wParam < 32) break;
3121 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3122 LOWORD(msg.wParam), FALSE );
3123 if (pos == (UINT)-2) fEndMenu = TRUE;
3124 else if (pos == (UINT)-1) MessageBeep(0);
3127 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3129 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3130 fEndMenu = (executedMenuId != -1);
3134 } /* switch(msg.message) - kbd */
3138 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3139 DispatchMessageW( &msg );
3143 if (!fEndMenu) fRemove = TRUE;
3145 /* finally remove message from the queue */
3147 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3148 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3149 else mt.trackFlags &= ~TF_SKIPREMOVE;
3152 MENU_SetCapture(0); /* release the capture */
3154 /* If dropdown is still painted and the close box is clicked on
3155 then the menu will be destroyed as part of the DispatchMessage above.
3156 This will then invalidate the menu handle in mt.hTopMenu. We should
3157 check for this first. */
3158 if( IsMenu( mt.hTopMenu ) )
3160 menu = MENU_GetMenu( mt.hTopMenu );
3162 if( IsWindow( mt.hOwnerWnd ) )
3164 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3166 if (menu && (menu->wFlags & MF_POPUP))
3168 DestroyWindow( menu->hWnd );
3171 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3172 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3175 /* Reset the variable for hiding menu */
3176 if( menu ) menu->bTimeToHide = FALSE;
3179 /* The return value is only used by TrackPopupMenu */
3180 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3181 if (executedMenuId == -1) executedMenuId = 0;
3182 return executedMenuId;
3185 /***********************************************************************
3188 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3192 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3196 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3197 if (!(wFlags & TPM_NONOTIFY))
3198 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3200 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3202 if (!(wFlags & TPM_NONOTIFY))
3204 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3205 /* If an app changed/recreated menu bar entries in WM_INITMENU
3206 * menu sizes will be recalculated once the menu created/shown.
3210 /* This makes the menus of applications built with Delphi work.
3211 * It also enables menus to be displayed in more than one window,
3212 * but there are some bugs left that need to be fixed in this case.
3214 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3218 /***********************************************************************
3221 static BOOL MENU_ExitTracking(HWND hWnd)
3223 TRACE("hwnd=%p\n", hWnd);
3225 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3231 /***********************************************************************
3232 * MENU_TrackMouseMenuBar
3234 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3236 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3238 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3239 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3241 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3245 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3246 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3247 MENU_ExitTracking(hWnd);
3252 /***********************************************************************
3253 * MENU_TrackKbdMenuBar
3255 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3257 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3259 UINT uItem = NO_SELECTED_ITEM;
3261 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3263 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3265 /* find window that has a menu */
3267 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3268 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3270 /* check if we have to track a system menu */
3272 hTrackMenu = GetMenu( hwnd );
3273 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3275 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3276 hTrackMenu = get_win_sys_menu( hwnd );
3278 wParam |= HTSYSMENU; /* prevent item lookup */
3281 if (!IsMenu( hTrackMenu )) return;
3283 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3285 if( wChar && wChar != ' ' )
3287 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3288 if ( uItem >= (UINT)(-2) )
3290 if( uItem == (UINT)(-1) ) MessageBeep(0);
3291 /* schedule end of menu tracking */
3292 wFlags |= TF_ENDMENU;
3297 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3299 if (wParam & HTSYSMENU)
3301 /* prevent sysmenu activation for managed windows on Alt down/up */
3302 if (GetPropA( hwnd, "__wine_x11_managed" ))
3303 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3307 if( uItem == NO_SELECTED_ITEM )
3308 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3310 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3314 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3315 MENU_ExitTracking( hwnd );
3319 /**********************************************************************
3320 * TrackPopupMenu (USER32.@)
3322 * Like the win32 API, the function return the command ID only if the
3323 * flag TPM_RETURNCMD is on.
3326 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3327 INT nReserved, HWND hWnd, const RECT *lpRect )
3331 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3334 if (!(wFlags & TPM_NONOTIFY))
3335 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3337 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3338 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3339 MENU_ExitTracking(hWnd);
3344 /**********************************************************************
3345 * TrackPopupMenuEx (USER32.@)
3347 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3348 HWND hWnd, LPTPMPARAMS lpTpm )
3350 FIXME("not fully implemented\n" );
3351 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3352 lpTpm ? &lpTpm->rcExclude : NULL );
3355 /***********************************************************************
3358 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3360 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3362 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3368 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3369 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3373 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3374 return MA_NOACTIVATE;
3379 BeginPaint( hwnd, &ps );
3380 MENU_DrawPopupMenu( hwnd, ps.hdc,
3381 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3382 EndPaint( hwnd, &ps );
3389 /* zero out global pointer in case resident popup window was destroyed. */
3390 if (hwnd == top_popup) top_popup = 0;
3397 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3400 SetWindowLongPtrW( hwnd, 0, 0 );
3403 case MM_SETMENUHANDLE:
3404 SetWindowLongPtrW( hwnd, 0, wParam );
3407 case MM_GETMENUHANDLE:
3408 return GetWindowLongPtrW( hwnd, 0 );
3411 return DefWindowProcW( hwnd, message, wParam, lParam );
3417 /***********************************************************************
3418 * MENU_GetMenuBarHeight
3420 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3422 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3423 INT orgX, INT orgY )
3429 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3431 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3433 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3434 SelectObject( hdc, get_menu_font(FALSE));
3435 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3436 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3437 ReleaseDC( hwnd, hdc );
3438 return lppop->Height;
3442 /*******************************************************************
3443 * ChangeMenuA (USER32.@)
3445 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3446 UINT id, UINT flags )
3448 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3449 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3451 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3452 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3454 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3455 flags & MF_BYPOSITION ? pos : id,
3456 flags & ~MF_REMOVE );
3457 /* Default: MF_INSERT */
3458 return InsertMenuA( hMenu, pos, flags, id, data );
3462 /*******************************************************************
3463 * ChangeMenuW (USER32.@)
3465 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3466 UINT id, UINT flags )
3468 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3469 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3471 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3472 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3474 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3475 flags & MF_BYPOSITION ? pos : id,
3476 flags & ~MF_REMOVE );
3477 /* Default: MF_INSERT */
3478 return InsertMenuW( hMenu, pos, flags, id, data );
3482 /*******************************************************************
3483 * CheckMenuItem (USER32.@)
3485 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3490 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3491 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3492 ret = item->fState & MF_CHECKED;
3493 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3494 else item->fState &= ~MF_CHECKED;
3499 /**********************************************************************
3500 * EnableMenuItem (USER32.@)
3502 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3508 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3510 /* Get the Popupmenu to access the owner menu */
3511 if (!(menu = MENU_GetMenu(hMenu)))
3514 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3517 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3518 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3520 /* If the close item in the system menu change update the close button */
3521 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3523 if (menu->hSysMenuOwner != 0)
3526 POPUPMENU* parentMenu;
3528 /* Get the parent menu to access*/
3529 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3532 /* Refresh the frame to reflect the change */
3533 GetWindowRect(parentMenu->hWnd, &rc);
3534 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3536 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3544 /*******************************************************************
3545 * GetMenuStringA (USER32.@)
3547 INT WINAPI GetMenuStringA(
3548 HMENU hMenu, /* [in] menuhandle */
3549 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3550 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3551 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3552 UINT wFlags /* [in] MF_ flags */
3556 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3557 if (str && nMaxSiz) str[0] = '\0';
3558 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3559 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3562 if (!item->text) return 0;
3563 if (!str || !nMaxSiz) return strlenW(item->text);
3564 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3566 TRACE("returning '%s'\n", str );
3571 /*******************************************************************
3572 * GetMenuStringW (USER32.@)
3574 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3575 LPWSTR str, INT nMaxSiz, UINT wFlags )
3579 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3580 if (str && nMaxSiz) str[0] = '\0';
3581 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3582 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3585 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3586 if( !(item->text)) {
3590 lstrcpynW( str, item->text, nMaxSiz );
3591 return strlenW(str);
3595 /**********************************************************************
3596 * HiliteMenuItem (USER32.@)
3598 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3602 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3603 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3604 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3605 if (menu->FocusedItem == wItemID) return TRUE;
3606 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3607 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3612 /**********************************************************************
3613 * GetMenuState (USER32.@)
3615 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3618 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3619 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3620 debug_print_menuitem (" item: ", item, "");
3621 if (item->fType & MF_POPUP)
3623 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3624 if (!menu) return -1;
3625 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3629 /* We used to (from way back then) mask the result to 0xff. */
3630 /* I don't know why and it seems wrong as the documented */
3631 /* return flag MF_SEPARATOR is outside that mask. */
3632 return (item->fType | item->fState);
3637 /**********************************************************************
3638 * GetMenuItemCount (USER32.@)
3640 INT WINAPI GetMenuItemCount( HMENU hMenu )
3642 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3643 if (!menu) return -1;
3644 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3645 return menu->nItems;
3649 /**********************************************************************
3650 * GetMenuItemID (USER32.@)
3652 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3656 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3657 if (lpmi->fType & MF_POPUP) return -1;
3663 /*******************************************************************
3664 * InsertMenuW (USER32.@)
3666 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3667 UINT_PTR id, LPCWSTR str )
3671 if (IS_STRING_ITEM(flags) && str)
3672 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3673 hMenu, pos, flags, id, debugstr_w(str) );
3674 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3675 hMenu, pos, flags, id, str );
3677 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3679 if (!(MENU_SetItemData( item, flags, id, str )))
3681 RemoveMenu( hMenu, pos, flags );
3685 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3686 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3688 item->hCheckBit = item->hUnCheckBit = 0;
3693 /*******************************************************************
3694 * InsertMenuA (USER32.@)
3696 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3697 UINT_PTR id, LPCSTR str )
3701 if (IS_STRING_ITEM(flags) && str)
3703 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3704 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3707 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3708 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3709 HeapFree( GetProcessHeap(), 0, newstr );
3713 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3717 /*******************************************************************
3718 * AppendMenuA (USER32.@)
3720 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3721 UINT_PTR id, LPCSTR data )
3723 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3727 /*******************************************************************
3728 * AppendMenuW (USER32.@)
3730 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3731 UINT_PTR id, LPCWSTR data )
3733 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3737 /**********************************************************************
3738 * RemoveMenu (USER32.@)
3740 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3745 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3746 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3747 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3751 MENU_FreeItemData( item );
3753 if (--menu->nItems == 0)
3755 HeapFree( GetProcessHeap(), 0, menu->items );
3760 while(nPos < menu->nItems)
3766 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3767 menu->nItems * sizeof(MENUITEM) );
3773 /**********************************************************************
3774 * DeleteMenu (USER32.@)
3776 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3778 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3779 if (!item) return FALSE;
3780 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3781 /* nPos is now the position of the item */
3782 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3787 /*******************************************************************
3788 * ModifyMenuW (USER32.@)
3790 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3791 UINT_PTR id, LPCWSTR str )
3795 if (IS_STRING_ITEM(flags))
3796 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3798 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3800 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3801 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3802 return MENU_SetItemData( item, flags, id, str );
3806 /*******************************************************************
3807 * ModifyMenuA (USER32.@)
3809 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3810 UINT_PTR id, LPCSTR str )
3814 if (IS_STRING_ITEM(flags) && str)
3816 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3817 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3820 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3821 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3822 HeapFree( GetProcessHeap(), 0, newstr );
3826 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3830 /**********************************************************************
3831 * CreatePopupMenu (USER32.@)
3833 HMENU WINAPI CreatePopupMenu(void)
3838 if (!(hmenu = CreateMenu())) return 0;
3839 menu = MENU_GetMenu( hmenu );
3840 menu->wFlags |= MF_POPUP;
3841 menu->bTimeToHide = FALSE;
3846 /**********************************************************************
3847 * GetMenuCheckMarkDimensions (USER.417)
3848 * GetMenuCheckMarkDimensions (USER32.@)
3850 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3852 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3856 /**********************************************************************
3857 * SetMenuItemBitmaps (USER32.@)
3859 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3860 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3863 TRACE("(%p, %04x, %04x, %p, %p)\n",
3864 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3865 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3867 if (!hNewCheck && !hNewUnCheck)
3869 item->fState &= ~MF_USECHECKBITMAPS;
3871 else /* Install new bitmaps */
3873 item->hCheckBit = hNewCheck;
3874 item->hUnCheckBit = hNewUnCheck;
3875 item->fState |= MF_USECHECKBITMAPS;
3881 /**********************************************************************
3882 * CreateMenu (USER32.@)
3884 HMENU WINAPI CreateMenu(void)
3888 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3889 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3891 ZeroMemory(menu, sizeof(POPUPMENU));
3892 menu->wMagic = MENU_MAGIC;
3893 menu->FocusedItem = NO_SELECTED_ITEM;
3894 menu->bTimeToHide = FALSE;
3896 TRACE("return %p\n", hMenu );
3902 /**********************************************************************
3903 * DestroyMenu (USER32.@)
3905 BOOL WINAPI DestroyMenu( HMENU hMenu )
3907 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3909 TRACE("(%p)\n", hMenu);
3912 if (!lppop) return FALSE;
3914 lppop->wMagic = 0; /* Mark it as destroyed */
3916 /* DestroyMenu should not destroy system menu popup owner */
3917 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3919 DestroyWindow( lppop->hWnd );
3923 if (lppop->items) /* recursively destroy submenus */
3926 MENUITEM *item = lppop->items;
3927 for (i = lppop->nItems; i > 0; i--, item++)
3929 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3930 MENU_FreeItemData( item );
3932 HeapFree( GetProcessHeap(), 0, lppop->items );
3934 USER_HEAP_FREE( hMenu );
3939 /**********************************************************************
3940 * GetSystemMenu (USER32.@)
3942 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3944 WND *wndPtr = WIN_GetPtr( hWnd );
3947 if (wndPtr == WND_DESKTOP) return 0;
3948 if (wndPtr == WND_OTHER_PROCESS)
3950 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3954 if (wndPtr->hSysMenu && bRevert)
3956 DestroyMenu(wndPtr->hSysMenu);
3957 wndPtr->hSysMenu = 0;
3960 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3961 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3963 if( wndPtr->hSysMenu )
3966 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3968 /* Store the dummy sysmenu handle to facilitate the refresh */
3969 /* of the close button if the SC_CLOSE item change */
3970 menu = MENU_GetMenu(retvalue);
3972 menu->hSysMenuOwner = wndPtr->hSysMenu;
3974 WIN_ReleasePtr( wndPtr );
3976 return bRevert ? 0 : retvalue;
3980 /*******************************************************************
3981 * SetSystemMenu (USER32.@)
3983 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3985 WND *wndPtr = WIN_GetPtr( hwnd );
3987 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3989 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3990 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3991 WIN_ReleasePtr( wndPtr );
3998 /**********************************************************************
3999 * GetMenu (USER32.@)
4001 HMENU WINAPI GetMenu( HWND hWnd )
4003 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4004 TRACE("for %p returning %p\n", hWnd, retvalue);
4008 /**********************************************************************
4009 * GetMenuBarInfo (USER32.@)
4011 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4013 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4017 /**********************************************************************
4020 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4021 * SetWindowPos call that would result if SetMenu were called directly.
4023 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4025 TRACE("(%p, %p);\n", hWnd, hMenu);
4027 if (hMenu && !IsMenu(hMenu))
4029 WARN("hMenu %p is not a menu handle\n", hMenu);
4030 SetLastError(ERROR_INVALID_MENU_HANDLE);
4033 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4036 hWnd = WIN_GetFullHandle( hWnd );
4037 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4043 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4045 lpmenu->hWnd = hWnd;
4046 lpmenu->Height = 0; /* Make sure we recalculate the size */
4048 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4053 /**********************************************************************
4054 * SetMenu (USER32.@)
4056 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4058 if(!MENU_SetMenu(hWnd, hMenu))
4061 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4062 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4067 /**********************************************************************
4068 * GetSubMenu (USER32.@)
4070 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4074 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4075 if (!(lpmi->fType & MF_POPUP)) return 0;
4076 return lpmi->hSubMenu;
4080 /**********************************************************************
4081 * DrawMenuBar (USER32.@)
4083 BOOL WINAPI DrawMenuBar( HWND hWnd )
4086 HMENU hMenu = GetMenu(hWnd);
4088 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4090 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4092 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4093 lppop->hwndOwner = hWnd;
4094 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4095 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4099 /***********************************************************************
4100 * DrawMenuBarTemp (USER32.@)
4104 * called by W98SE desk.cpl Control Panel Applet
4106 * Not 100% sure about the param names, but close.
4108 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4113 BOOL flat_menu = FALSE;
4115 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4118 hMenu = GetMenu(hwnd);
4121 hFont = get_menu_font(FALSE);
4123 lppop = MENU_GetMenu( hMenu );
4124 if (lppop == NULL || lprect == NULL)
4126 retvalue = GetSystemMetrics(SM_CYMENU);
4130 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4132 hfontOld = SelectObject( hDC, hFont);
4134 if (lppop->Height == 0)
4135 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4137 lprect->bottom = lprect->top + lppop->Height;
4139 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4141 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4142 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4143 LineTo( hDC, lprect->right, lprect->bottom );
4145 if (lppop->nItems == 0)
4147 retvalue = GetSystemMetrics(SM_CYMENU);
4151 for (i = 0; i < lppop->nItems; i++)
4153 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4154 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4156 retvalue = lppop->Height;
4159 if (hfontOld) SelectObject (hDC, hfontOld);
4163 /***********************************************************************
4164 * EndMenu (USER.187)
4165 * EndMenu (USER32.@)
4167 void WINAPI EndMenu(void)
4169 /* if we are in the menu code, and it is active */
4170 if (!fEndMenu && top_popup)
4172 /* terminate the menu handling code */
4175 /* needs to be posted to wakeup the internal menu handler */
4176 /* which will now terminate the menu, in the event that */
4177 /* the main window was minimized, or lost focus, so we */
4178 /* don't end up with an orphaned menu */
4179 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4184 /***********************************************************************
4185 * LookupMenuHandle (USER.217)
4187 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4189 HMENU hmenu32 = HMENU_32(hmenu);
4191 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4192 else return HMENU_16(hmenu32);
4196 /**********************************************************************
4197 * LoadMenu (USER.150)
4199 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4205 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4206 if (!name) return 0;
4208 instance = GetExePtr( instance );
4209 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4210 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4211 hMenu = LoadMenuIndirect16(LockResource16(handle));
4212 FreeResource16( handle );
4217 /*****************************************************************
4218 * LoadMenuA (USER32.@)
4220 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4222 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4223 if (!hrsrc) return 0;
4224 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4228 /*****************************************************************
4229 * LoadMenuW (USER32.@)
4231 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4233 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4234 if (!hrsrc) return 0;
4235 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4239 /**********************************************************************
4240 * LoadMenuIndirect (USER.220)
4242 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4245 WORD version, offset;
4246 LPCSTR p = (LPCSTR)template;
4248 TRACE("(%p)\n", template );
4249 version = GET_WORD(p);
4253 WARN("version must be 0 for Win16\n" );
4256 offset = GET_WORD(p);
4257 p += sizeof(WORD) + offset;
4258 if (!(hMenu = CreateMenu())) return 0;
4259 if (!MENU_ParseResource( p, hMenu, FALSE ))
4261 DestroyMenu( hMenu );
4264 return HMENU_16(hMenu);
4268 /**********************************************************************
4269 * LoadMenuIndirectW (USER32.@)
4271 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4274 WORD version, offset;
4275 LPCSTR p = (LPCSTR)template;
4277 version = GET_WORD(p);
4279 TRACE("%p, ver %d\n", template, version );
4282 case 0: /* standard format is version of 0 */
4283 offset = GET_WORD(p);
4284 p += sizeof(WORD) + offset;
4285 if (!(hMenu = CreateMenu())) return 0;
4286 if (!MENU_ParseResource( p, hMenu, TRUE ))
4288 DestroyMenu( hMenu );
4292 case 1: /* extended format is version of 1 */
4293 offset = GET_WORD(p);
4294 p += sizeof(WORD) + offset;
4295 if (!(hMenu = CreateMenu())) return 0;
4296 if (!MENUEX_ParseResource( p, hMenu))
4298 DestroyMenu( hMenu );
4303 ERR("version %d not supported.\n", version);
4309 /**********************************************************************
4310 * LoadMenuIndirectA (USER32.@)
4312 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4314 return LoadMenuIndirectW( template );
4318 /**********************************************************************
4321 BOOL WINAPI IsMenu(HMENU hmenu)
4323 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4324 return menu != NULL;
4327 /**********************************************************************
4328 * GetMenuItemInfo_common
4331 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4332 LPMENUITEMINFOW lpmii, BOOL unicode)
4334 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4336 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4341 if( lpmii->fMask & MIIM_TYPE) {
4342 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4343 WARN("invalid combination of fMask bits used\n");
4344 /* this does not happen on Win9x/ME */
4345 SetLastError( ERROR_INVALID_PARAMETER);
4348 lpmii->fType = menu->fType & ~MF_POPUP;
4349 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4350 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4351 if( lpmii->fType & MFT_BITMAP) {
4352 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4354 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4355 /* this does not happen on Win9x/ME */
4356 lpmii->dwTypeData = 0;
4361 /* copy the text string */
4362 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4364 if(lpmii->dwTypeData && lpmii->cch) {
4367 *((WCHAR *)lpmii->dwTypeData) = 0;
4369 *((CHAR *)lpmii->dwTypeData) = 0;
4375 len = strlenW(menu->text);
4376 if(lpmii->dwTypeData && lpmii->cch)
4377 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4381 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4382 0, NULL, NULL ) - 1;
4383 if(lpmii->dwTypeData && lpmii->cch)
4384 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4385 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4386 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4388 /* if we've copied a substring we return its length */
4389 if(lpmii->dwTypeData && lpmii->cch)
4390 if (lpmii->cch <= len + 1)
4395 /* return length of string */
4396 /* not on Win9x/ME if fType & MFT_BITMAP */
4402 if (lpmii->fMask & MIIM_FTYPE)
4403 lpmii->fType = menu->fType & ~MF_POPUP;
4405 if (lpmii->fMask & MIIM_BITMAP)
4406 lpmii->hbmpItem = menu->hbmpItem;
4408 if (lpmii->fMask & MIIM_STATE)
4409 lpmii->fState = menu->fState;
4411 if (lpmii->fMask & MIIM_ID)
4412 lpmii->wID = menu->wID;
4414 if (lpmii->fMask & MIIM_SUBMENU)
4415 lpmii->hSubMenu = menu->hSubMenu;
4417 /* hSubMenu is always cleared
4418 * (not on Win9x/ME ) */
4419 lpmii->hSubMenu = 0;
4422 if (lpmii->fMask & MIIM_CHECKMARKS) {
4423 lpmii->hbmpChecked = menu->hCheckBit;
4424 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4426 if (lpmii->fMask & MIIM_DATA)
4427 lpmii->dwItemData = menu->dwItemData;
4432 /**********************************************************************
4433 * GetMenuItemInfoA (USER32.@)
4435 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4436 LPMENUITEMINFOA lpmii)
4440 if( lpmii->cbSize != sizeof( mii) &&
4441 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4442 SetLastError( ERROR_INVALID_PARAMETER);
4445 memcpy( &mii, lpmii, lpmii->cbSize);
4446 mii.cbSize = sizeof( mii);
4447 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4448 (LPMENUITEMINFOW)&mii, FALSE);
4449 mii.cbSize = lpmii->cbSize;
4450 memcpy( lpmii, &mii, mii.cbSize);
4454 /**********************************************************************
4455 * GetMenuItemInfoW (USER32.@)
4457 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4458 LPMENUITEMINFOW lpmii)
4462 if( lpmii->cbSize != sizeof( mii) &&
4463 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4464 SetLastError( ERROR_INVALID_PARAMETER);
4467 memcpy( &mii, lpmii, lpmii->cbSize);
4468 mii.cbSize = sizeof( mii);
4469 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4470 mii.cbSize = lpmii->cbSize;
4471 memcpy( lpmii, &mii, mii.cbSize);
4476 /* set a menu item text from a ASCII or Unicode string */
4477 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4483 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4484 strcpyW( menu->text, text );
4488 LPCSTR str = (LPCSTR)text;
4489 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4490 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4491 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4496 /**********************************************************************
4497 * SetMenuItemInfo_common
4500 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4501 const MENUITEMINFOW *lpmii,
4504 if (!menu) return FALSE;
4506 debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4508 if (lpmii->fMask & MIIM_TYPE ) {
4509 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4510 WARN("invalid combination of fMask bits used\n");
4511 /* this does not happen on Win9x/ME */
4512 SetLastError( ERROR_INVALID_PARAMETER);
4515 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4516 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4517 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4519 if (IS_STRING_ITEM(menu->fType)) {
4520 HeapFree(GetProcessHeap(), 0, menu->text);
4521 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4522 } else if( (menu->fType) & MFT_BITMAP)
4523 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4526 if (lpmii->fMask & MIIM_FTYPE ) {
4527 if(( lpmii->fType & MFT_BITMAP)) {
4528 SetLastError( ERROR_INVALID_PARAMETER);
4531 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4532 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4534 if (lpmii->fMask & MIIM_STRING ) {
4535 /* free the string when used */
4536 HeapFree(GetProcessHeap(), 0, menu->text);
4537 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4540 if (lpmii->fMask & MIIM_STATE)
4542 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4543 menu->fState = lpmii->fState;
4546 if (lpmii->fMask & MIIM_ID)
4547 menu->wID = lpmii->wID;
4549 if (lpmii->fMask & MIIM_SUBMENU) {
4550 menu->hSubMenu = lpmii->hSubMenu;
4551 if (menu->hSubMenu) {
4552 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4554 subMenu->wFlags |= MF_POPUP;
4555 menu->fType |= MF_POPUP;
4558 SetLastError( ERROR_INVALID_PARAMETER);
4563 menu->fType &= ~MF_POPUP;
4566 if (lpmii->fMask & MIIM_CHECKMARKS)
4568 if (lpmii->fType & MFT_RADIOCHECK)
4569 menu->fType |= MFT_RADIOCHECK;
4571 menu->hCheckBit = lpmii->hbmpChecked;
4572 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4574 if (lpmii->fMask & MIIM_DATA)
4575 menu->dwItemData = lpmii->dwItemData;
4577 if (lpmii->fMask & MIIM_BITMAP)
4578 menu->hbmpItem = lpmii->hbmpItem;
4580 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4581 menu->fType |= MFT_SEPARATOR;
4583 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4587 /**********************************************************************
4588 * SetMenuItemInfoA (USER32.@)
4590 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4591 const MENUITEMINFOA *lpmii)
4594 if( lpmii->cbSize != sizeof( mii) &&
4595 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4596 SetLastError( ERROR_INVALID_PARAMETER);
4599 memcpy( &mii, lpmii, lpmii->cbSize);
4600 if( lpmii->cbSize != sizeof( mii)) {
4601 mii.cbSize = sizeof( mii);
4602 mii.hbmpItem = NULL;
4604 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4605 (const MENUITEMINFOW *)&mii, FALSE);
4608 /**********************************************************************
4609 * SetMenuItemInfoW (USER32.@)
4611 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4612 const MENUITEMINFOW *lpmii)
4615 if( lpmii->cbSize != sizeof( mii) &&
4616 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4617 SetLastError( ERROR_INVALID_PARAMETER);
4620 memcpy( &mii, lpmii, lpmii->cbSize);
4621 if( lpmii->cbSize != sizeof( mii)) {
4622 mii.cbSize = sizeof( mii);
4623 mii.hbmpItem = NULL;
4625 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4626 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4629 /**********************************************************************
4630 * SetMenuDefaultItem (USER32.@)
4633 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4639 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4641 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4643 /* reset all default-item flags */
4645 for (i = 0; i < menu->nItems; i++, item++)
4647 item->fState &= ~MFS_DEFAULT;
4650 /* no default item */
4659 if ( uItem >= menu->nItems ) return FALSE;
4660 item[uItem].fState |= MFS_DEFAULT;
4665 for (i = 0; i < menu->nItems; i++, item++)
4667 if (item->wID == uItem)
4669 item->fState |= MFS_DEFAULT;
4678 /**********************************************************************
4679 * GetMenuDefaultItem (USER32.@)
4681 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4687 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4689 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4691 /* find default item */
4695 if (! item) return -1;
4697 while ( !( item->fState & MFS_DEFAULT ) )
4700 if (i >= menu->nItems ) return -1;
4703 /* default: don't return disabled items */
4704 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4706 /* search rekursiv when needed */
4707 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4710 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4711 if ( -1 != ret ) return ret;
4713 /* when item not found in submenu, return the popup item */
4715 return ( bypos ) ? i : item->wID;
4720 /**********************************************************************
4721 * InsertMenuItemA (USER32.@)
4723 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4724 const MENUITEMINFOA *lpmii)
4726 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4728 if( lpmii->cbSize != sizeof( mii) &&
4729 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4730 SetLastError( ERROR_INVALID_PARAMETER);
4733 memcpy( &mii, lpmii, lpmii->cbSize);
4734 if( lpmii->cbSize != sizeof( mii)) {
4735 mii.cbSize = sizeof( mii);
4736 mii.hbmpItem = NULL;
4738 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4742 /**********************************************************************
4743 * InsertMenuItemW (USER32.@)
4745 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4746 const MENUITEMINFOW *lpmii)
4748 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4750 if( lpmii->cbSize != sizeof( mii) &&
4751 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4752 SetLastError( ERROR_INVALID_PARAMETER);
4755 memcpy( &mii, lpmii, lpmii->cbSize);
4756 if( lpmii->cbSize != sizeof( mii)) {
4757 mii.cbSize = sizeof( mii);
4758 mii.hbmpItem = NULL;
4760 return SetMenuItemInfo_common(item, &mii, TRUE);
4763 /**********************************************************************
4764 * CheckMenuRadioItem (USER32.@)
4767 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4768 UINT first, UINT last, UINT check,
4771 MENUITEM *mifirst, *milast, *micheck;
4772 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4774 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4776 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4777 milast = MENU_FindItem (&mlast, &last, bypos);
4778 micheck = MENU_FindItem (&mcheck, &check, bypos);
4780 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4781 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4782 micheck > milast || micheck < mifirst)
4785 while (mifirst <= milast)
4787 if (mifirst == micheck)
4789 mifirst->fType |= MFT_RADIOCHECK;
4790 mifirst->fState |= MFS_CHECKED;
4792 mifirst->fType &= ~MFT_RADIOCHECK;
4793 mifirst->fState &= ~MFS_CHECKED;
4802 /**********************************************************************
4803 * GetMenuItemRect (USER32.@)
4805 * ATTENTION: Here, the returned values in rect are the screen
4806 * coordinates of the item just like if the menu was
4807 * always on the upper left side of the application.
4810 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4813 POPUPMENU *itemMenu;
4817 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4819 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4820 referenceHwnd = hwnd;
4824 itemMenu = MENU_GetMenu(hMenu);
4825 if (itemMenu == NULL)
4828 if(itemMenu->hWnd == 0)
4830 referenceHwnd = itemMenu->hWnd;
4833 if ((rect == NULL) || (item == NULL))
4838 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4844 /**********************************************************************
4845 * SetMenuInfo (USER32.@)
4848 * MIM_APPLYTOSUBMENUS
4849 * actually use the items to draw the menu
4851 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4855 TRACE("(%p %p)\n", hMenu, lpmi);
4857 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4860 if (lpmi->fMask & MIM_BACKGROUND)
4861 menu->hbrBack = lpmi->hbrBack;
4863 if (lpmi->fMask & MIM_HELPID)
4864 menu->dwContextHelpID = lpmi->dwContextHelpID;
4866 if (lpmi->fMask & MIM_MAXHEIGHT)
4867 menu->cyMax = lpmi->cyMax;
4869 if (lpmi->fMask & MIM_MENUDATA)
4870 menu->dwMenuData = lpmi->dwMenuData;
4872 if (lpmi->fMask & MIM_STYLE)
4874 menu->dwStyle = lpmi->dwStyle;
4875 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4876 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4877 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4878 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4886 /**********************************************************************
4887 * GetMenuInfo (USER32.@)
4893 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4896 TRACE("(%p %p)\n", hMenu, lpmi);
4898 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4901 if (lpmi->fMask & MIM_BACKGROUND)
4902 lpmi->hbrBack = menu->hbrBack;
4904 if (lpmi->fMask & MIM_HELPID)
4905 lpmi->dwContextHelpID = menu->dwContextHelpID;
4907 if (lpmi->fMask & MIM_MAXHEIGHT)
4908 lpmi->cyMax = menu->cyMax;
4910 if (lpmi->fMask & MIM_MENUDATA)
4911 lpmi->dwMenuData = menu->dwMenuData;
4913 if (lpmi->fMask & MIM_STYLE)
4914 lpmi->dwStyle = menu->dwStyle;
4922 /**********************************************************************
4923 * SetMenuContextHelpId (USER32.@)
4925 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4929 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4931 if ((menu = MENU_GetMenu(hMenu)))
4933 menu->dwContextHelpID = dwContextHelpID;
4940 /**********************************************************************
4941 * GetMenuContextHelpId (USER32.@)
4943 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4947 TRACE("(%p)\n", hMenu);
4949 if ((menu = MENU_GetMenu(hMenu)))
4951 return menu->dwContextHelpID;
4956 /**********************************************************************
4957 * MenuItemFromPoint (USER32.@)
4959 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4961 POPUPMENU *menu = MENU_GetMenu(hMenu);
4964 /*FIXME: Do we have to handle hWnd here? */
4965 if (!menu) return -1;
4966 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4971 /**********************************************************************
4972 * translate_accelerator
4974 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4975 BYTE fVirt, WORD key, WORD cmd )
4980 if (wParam != key) return FALSE;
4982 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4983 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4984 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4986 if (message == WM_CHAR || message == WM_SYSCHAR)
4988 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4990 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4996 if(fVirt & FVIRTKEY)
4998 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4999 wParam, 0xff & HIWORD(lParam));
5001 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5002 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5006 if (!(lParam & 0x01000000)) /* no special_key */
5008 if ((fVirt & FALT) && (lParam & 0x20000000))
5009 { /* ^^ ALT pressed */
5010 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5019 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5023 HMENU hMenu, hSubMenu, hSysMenu;
5024 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5026 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5027 hSysMenu = get_win_sys_menu( hWnd );
5029 /* find menu item and ask application to initialize it */
5030 /* 1. in the system menu */
5031 hSubMenu = hSysMenu;
5033 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5037 if (!IsWindowEnabled(hWnd))
5041 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5042 if(hSubMenu != hSysMenu)
5044 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5045 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5046 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5048 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5051 else /* 2. in the window's menu */
5055 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5059 if (!IsWindowEnabled(hWnd))
5063 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5064 if(hSubMenu != hMenu)
5066 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5067 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5068 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5070 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5077 if (uSysStat != (UINT)-1)
5079 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5086 if (uStat != (UINT)-1)
5092 if (uStat & (MF_DISABLED|MF_GRAYED))
5104 if( mesg==WM_COMMAND )
5106 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5107 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5109 else if( mesg==WM_SYSCOMMAND )
5111 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5112 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5116 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5117 * #0: unknown (please report!)
5118 * #1: for WM_KEYUP,WM_SYSKEYUP
5119 * #2: mouse is captured
5120 * #3: window is disabled
5121 * #4: it's a disabled system menu option
5122 * #5: it's a menu option, but window is iconic
5123 * #6: it's a menu option, but disabled
5125 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5127 ERR_(accel)(" unknown reason - please report!\n");
5132 /**********************************************************************
5133 * TranslateAcceleratorA (USER32.@)
5134 * TranslateAccelerator (USER32.@)
5136 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5139 LPACCEL16 lpAccelTbl;
5143 if (!hWnd || !msg) return 0;
5145 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5147 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5151 wParam = msg->wParam;
5153 switch (msg->message)
5162 char ch = LOWORD(wParam);
5164 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5165 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5173 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5174 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5178 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5179 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5181 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5186 /**********************************************************************
5187 * TranslateAcceleratorW (USER32.@)
5189 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5192 LPACCEL16 lpAccelTbl;
5195 if (!hWnd || !msg) return 0;
5197 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5199 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5203 switch (msg->message)
5215 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5216 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5220 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5221 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5223 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);