4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
52 #include "wine/winbase16.h"
53 #include "wine/winuser16.h"
55 #include "wine/server.h"
56 #include "wine/unicode.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 /* Popup menu structure */
92 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
93 WORD wMagic; /* Magic number */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 SIZE maxBmpSize; /* Maximum size of the bitmap items */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x0001
118 #define TF_SUSPENDPOPUP 0x0002
119 #define TF_SKIPREMOVE 0x0004
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
130 #define MENU_MAGIC 0x554d /* 'MU' */
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
138 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
139 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* top and bottom margins for popup menus */
145 #define MENU_TOP_MARGIN 3
146 #define MENU_BOTTOM_MARGIN 2
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
155 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
156 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define MENUITEMINFO_TYPE_MASK \
162 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
165 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
166 #define STATE_MASK (~TYPE_MASK)
167 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
169 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
171 static SIZE menucharsize;
172 static UINT ODitemheight; /* default owner drawn item height */
174 /* Use global popup window because there's no way 2 menus can
175 * be tracked at the same time. */
176 static HWND top_popup;
178 /* Flag set by EndMenu() to force an exit from menu tracking */
179 static BOOL fEndMenu = FALSE;
181 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
183 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
185 /*********************************************************************
186 * menu class descriptor
188 const struct builtin_class_descr MENU_builtin_class =
190 POPUPMENU_CLASS_ATOMA, /* name */
191 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
192 NULL, /* procA (winproc is Unicode only) */
193 PopupMenuWndProc, /* procW */
194 sizeof(HMENU), /* extra */
195 IDC_ARROW, /* cursor */
196 (HBRUSH)(COLOR_MENU+1) /* brush */
200 /***********************************************************************
201 * debug_print_menuitem
203 * Print a menuitem in readable form.
206 #define debug_print_menuitem(pre, mp, post) \
207 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
209 #define MENUOUT(text) \
210 TRACE("%s%s", (count++ ? "," : ""), (text))
212 #define MENUFLAG(bit,text) \
214 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
220 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
221 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
222 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
223 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
224 TRACE("%s ", prefix);
226 UINT flags = mp->fType;
227 TRACE( "{ ID=0x%x", mp->wID);
229 TRACE( ", Sub=%p", mp->hSubMenu);
233 MENUFLAG( MFT_SEPARATOR, "sep");
234 MENUFLAG( MFT_OWNERDRAW, "own");
235 MENUFLAG( MFT_BITMAP, "bit");
236 MENUFLAG(MF_POPUP, "pop");
237 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
238 MENUFLAG(MFT_MENUBREAK, "brk");
239 MENUFLAG(MFT_RADIOCHECK, "radio");
240 MENUFLAG(MFT_RIGHTORDER, "rorder");
241 MENUFLAG(MF_SYSMENU, "sys");
242 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
244 TRACE( "+0x%x", flags);
250 MENUFLAG(MFS_GRAYED, "grey");
251 MENUFLAG(MFS_DEFAULT, "default");
252 MENUFLAG(MFS_DISABLED, "dis");
253 MENUFLAG(MFS_CHECKED, "check");
254 MENUFLAG(MFS_HILITE, "hi");
255 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
256 MENUFLAG(MF_MOUSESELECT, "mouse");
258 TRACE( "+0x%x", flags);
261 TRACE( ", Chk=%p", mp->hCheckBit);
263 TRACE( ", Unc=%p", mp->hUnCheckBit);
265 TRACE( ", Text=%s", debugstr_w(mp->text));
267 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270 if( IS_MAGIC_BITMAP(mp->hbmpItem))
271 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
273 TRACE( ", hbitmap=%p", mp->hbmpItem);
278 TRACE(" %s\n", postfix);
285 /***********************************************************************
288 * Validate the given menu handle and returns the menu structure pointer.
290 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
292 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
293 if (!menu || menu->wMagic != MENU_MAGIC)
295 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
301 /***********************************************************************
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
309 WND *win = WIN_GetPtr( hwnd );
310 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
313 WIN_ReleasePtr( win );
318 /***********************************************************************
321 static HFONT get_menu_font( BOOL bold )
323 static HFONT hMenuFont, hMenuFontBold;
325 HFONT ret = bold ? hMenuFontBold : hMenuFont;
329 NONCLIENTMETRICSW ncm;
332 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
337 ncm.lfMenuFont.lfWeight += 300;
338 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
340 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
341 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
345 /* another thread beat us to it */
353 /***********************************************************************
356 static HBITMAP get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap;
360 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap;
371 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap;
382 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap;
393 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap;
404 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
408 /***********************************************************************
411 * Return the default system menu.
413 static HMENU MENU_CopySysPopup(void)
415 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
416 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
419 POPUPMENU* menu = MENU_GetMenu(hMenu);
420 menu->wFlags |= MF_SYSMENU | MF_POPUP;
421 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
424 ERR("Unable to load default system menu\n" );
426 TRACE("returning %p.\n", hMenu );
432 /**********************************************************************
435 * Create a copy of the system menu. System menu in Windows is
436 * a special menu bar with the single entry - system menu popup.
437 * This popup is presented to the outside world as a "system menu".
438 * However, the real system menu handle is sometimes seen in the
439 * WM_MENUSELECT parameters (and Word 6 likes it this way).
441 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
445 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
446 if ((hMenu = CreateMenu()))
448 POPUPMENU *menu = MENU_GetMenu(hMenu);
449 menu->wFlags = MF_SYSMENU;
450 menu->hWnd = WIN_GetFullHandle( hWnd );
451 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
454 hPopupMenu = MENU_CopySysPopup();
458 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
459 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
461 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
462 (UINT_PTR)hPopupMenu, NULL );
464 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
465 menu->items[0].fState = 0;
466 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
468 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471 DestroyMenu( hMenu );
473 ERR("failed to load system menu!\n");
478 /***********************************************************************
479 * MENU_InitSysMenuPopup
481 * Grey the appropriate items in System menu.
483 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
487 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
488 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
489 gray = ((style & WS_MAXIMIZE) != 0);
490 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
492 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
494 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
496 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = (clsStyle & CS_NOCLOSE) != 0;
499 /* The menu item must keep its state if it's disabled */
501 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
505 /******************************************************************************
507 * UINT MENU_GetStartOfNextColumn(
510 *****************************************************************************/
512 static UINT MENU_GetStartOfNextColumn(
515 POPUPMENU *menu = MENU_GetMenu(hMenu);
519 return NO_SELECTED_ITEM;
521 i = menu->FocusedItem + 1;
522 if( i == NO_SELECTED_ITEM )
525 for( ; i < menu->nItems; ++i ) {
526 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
530 return NO_SELECTED_ITEM;
534 /******************************************************************************
536 * UINT MENU_GetStartOfPrevColumn(
539 *****************************************************************************/
541 static UINT MENU_GetStartOfPrevColumn(
544 POPUPMENU *menu = MENU_GetMenu(hMenu);
548 return NO_SELECTED_ITEM;
550 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
551 return NO_SELECTED_ITEM;
553 /* Find the start of the column */
555 for(i = menu->FocusedItem; i != 0 &&
556 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
560 return NO_SELECTED_ITEM;
562 for(--i; i != 0; --i) {
563 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
567 TRACE("ret %d.\n", i );
574 /***********************************************************************
577 * Find a menu item. Return a pointer on the item, and modifies *hmenu
578 * in case the item was in a sub-menu.
580 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
583 MENUITEM *fallback = NULL;
584 UINT fallback_pos = 0;
587 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
588 if (wFlags & MF_BYPOSITION)
590 if (*nPos >= menu->nItems) return NULL;
591 return &menu->items[*nPos];
595 MENUITEM *item = menu->items;
596 for (i = 0; i < menu->nItems; i++, item++)
598 if (item->fType & MF_POPUP)
600 HMENU hsubmenu = item->hSubMenu;
601 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
607 else if (item->wID == *nPos)
609 /* fallback to this item if nothing else found */
614 else if (item->wID == *nPos)
623 *nPos = fallback_pos;
628 /***********************************************************************
631 * Find a Sub menu. Return the position of the submenu, and modifies
632 * *hmenu in case it is found in another sub-menu.
633 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
635 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
640 if (((*hmenu)==(HMENU)0xffff) ||
641 (!(menu = MENU_GetMenu(*hmenu))))
642 return NO_SELECTED_ITEM;
644 for (i = 0; i < menu->nItems; i++, item++) {
645 if(!(item->fType & MF_POPUP)) continue;
646 if (item->hSubMenu == hSubTarget) {
650 HMENU hsubmenu = item->hSubMenu;
651 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
652 if (pos != NO_SELECTED_ITEM) {
658 return NO_SELECTED_ITEM;
661 /***********************************************************************
664 static void MENU_FreeItemData( MENUITEM* item )
667 HeapFree( GetProcessHeap(), 0, item->text );
670 /***********************************************************************
671 * MENU_AdjustMenuItemRect
673 * Adjust menu item rectangle according to scrolling state.
676 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
678 if (menu->bScrolling)
680 UINT arrow_bitmap_width, arrow_bitmap_height;
683 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
684 arrow_bitmap_width = bmp.bmWidth;
685 arrow_bitmap_height = bmp.bmHeight;
686 rect->top += arrow_bitmap_height - menu->nScrollPos;
687 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
692 /***********************************************************************
693 * MENU_FindItemByCoords
695 * Find the item at the specified coordinates (screen coords). Does
696 * not work for child windows and therefore should not be called for
697 * an arbitrary system menu.
699 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
700 POINT pt, UINT *pos )
707 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
708 pt.x -= wrect.left;pt.y -= wrect.top;
710 for (i = 0; i < menu->nItems; i++, item++)
713 MENU_AdjustMenuItemRect(menu, &rect);
714 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
715 (pt.y >= rect.top) && (pt.y < rect.bottom))
725 /***********************************************************************
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
732 WCHAR key, BOOL forceMenuChar )
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
740 POPUPMENU *menu = MENU_GetMenu( hmenu );
741 MENUITEM *item = menu->items;
748 for (i = 0; i < menu->nItems; i++, item++)
752 WCHAR *p = item->text - 2;
755 p = strchrW (p + 2, '&');
757 while (p != NULL && p [1] == '&');
758 if (p && (toupperW(p[1]) == toupperW(key))) return i;
762 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
763 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
764 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
765 if (HIWORD(menuchar) == 1) return (UINT)(-2);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
780 HBITMAP bmp = lpitem->hbmpItem;
782 size->cx = size->cy = 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR) bmp )
787 case (INT_PTR)HBMMENU_CALLBACK:
789 MEASUREITEMSTRUCT measItem;
790 measItem.CtlType = ODT_MENU;
792 measItem.itemID = lpitem->wID;
793 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
794 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
795 measItem.itemData = lpitem->dwItemData;
796 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
797 size->cx = measItem.itemWidth;
798 size->cy = measItem.itemHeight;
802 case (INT_PTR)HBMMENU_SYSTEM:
803 if (lpitem->dwItemData)
805 bmp = (HBITMAP)lpitem->dwItemData;
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821 FIXME("Magic %p not implemented\n", bmp );
824 if (GetObjectW(bmp, sizeof(bm), &bm ))
826 size->cx = bm.bmWidth;
827 size->cy = bm.bmHeight;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
837 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
843 int w = rect->right - rect->left;
844 int h = rect->bottom - rect->top;
847 HBITMAP hbmToDraw = lpitem->hbmpItem;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw))
856 switch((INT_PTR)hbmToDraw)
858 case (INT_PTR)HBMMENU_SYSTEM:
859 if (lpitem->dwItemData)
861 bmp = (HBITMAP)lpitem->dwItemData;
862 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
866 static HBITMAP hBmpSysMenu;
868 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
870 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset = bm.bmWidth / 2;
873 bm.bmWidth -= bmp_xoffset;
876 case (INT_PTR)HBMMENU_MBAR_RESTORE:
877 flags = DFCS_CAPTIONRESTORE;
879 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
880 flags = DFCS_CAPTIONMIN;
882 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
883 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
885 case (INT_PTR)HBMMENU_MBAR_CLOSE:
886 flags = DFCS_CAPTIONCLOSE;
888 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
889 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
891 case (INT_PTR)HBMMENU_CALLBACK:
893 DRAWITEMSTRUCT drawItem;
894 drawItem.CtlType = ODT_MENU;
896 drawItem.itemID = lpitem->wID;
897 drawItem.itemAction = odaction;
898 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
899 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
900 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
903 drawItem.hwndItem = (HWND)hmenu;
905 drawItem.itemData = lpitem->dwItemData;
906 drawItem.rcItem = *rect;
907 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
911 case (INT_PTR)HBMMENU_POPUP_CLOSE:
912 case (INT_PTR)HBMMENU_POPUP_RESTORE:
913 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
914 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
916 FIXME("Magic %p not implemented\n", hbmToDraw);
920 InflateRect( &r, -1, -1 );
921 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
922 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
926 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
929 hdcMem = CreateCompatibleDC( hdc );
930 SelectObject( hdcMem, bmp );
932 /* handle fontsize > bitmap_height */
933 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
935 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
936 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
937 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
938 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
943 /***********************************************************************
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
949 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
952 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
953 UINT arrow_bitmap_width;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
959 (menuBar ? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
962 arrow_bitmap_width = bm.bmWidth;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize.cx ) {
966 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight = HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974 if (lpitem->fType & MF_OWNERDRAW)
976 MEASUREITEMSTRUCT mis;
977 mis.CtlType = ODT_MENU;
979 mis.itemID = lpitem->wID;
980 mis.itemData = lpitem->dwItemData;
981 mis.itemHeight = ODitemheight;
983 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
993 lpitem->rect.bottom += mis.itemHeight;
995 TRACE("id=%04x size=%dx%d\n",
996 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
997 lpitem->rect.bottom-lpitem->rect.top);
1001 if (lpitem->fType & MF_SEPARATOR)
1003 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1005 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1013 if (lpitem->hbmpItem) {
1016 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem->bmpsize = size;
1020 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1021 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1022 lpitem->rect.right += size.cx + 2;
1023 itemheight = size.cy + 2;
1025 if( !(lppop->dwStyle & MNS_NOCHECK))
1026 lpitem->rect.right += check_bitmap_width;
1027 lpitem->rect.right += 4 + menucharsize.cx;
1028 lpitem->xTab = lpitem->rect.right;
1029 lpitem->rect.right += arrow_bitmap_width;
1030 } else if (lpitem->hbmpItem) { /* menuBar */
1033 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1034 lpitem->bmpsize = size;
1035 lpitem->rect.right += size.cx;
1036 if( lpitem->text) lpitem->rect.right += 2;
1037 itemheight = size.cy;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1042 HFONT hfontOld = NULL;
1043 RECT rc = lpitem->rect;
1044 LONG txtheight, txtwidth;
1046 if ( lpitem->fState & MFS_DEFAULT ) {
1047 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1050 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1051 DT_SINGLELINE|DT_CALCRECT);
1052 lpitem->rect.right += rc.right - rc.left;
1053 itemheight = max( max( itemheight, txtheight),
1054 GetSystemMetrics( SM_CYMENU) - 1);
1055 lpitem->rect.right += 2 * menucharsize.cx;
1057 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1060 int n = (int)( p - lpitem->text);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1064 DT_SINGLELINE|DT_CALCRECT);
1065 txtwidth = rc.right - rc.left;
1066 p += 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1069 DT_SINGLELINE|DT_CALCRECT);
1070 lpitem->xTab += txtwidth;
1071 txtheight = max( txtheight, tmpheight);
1072 txtwidth += menucharsize.cx + /* space for the tab */
1073 tmprc.right - tmprc.left; /* space for the short cut */
1075 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1076 DT_SINGLELINE|DT_CALCRECT);
1077 txtwidth = rc.right - rc.left;
1078 lpitem->xTab += txtwidth;
1080 lpitem->rect.right += 2 + txtwidth;
1081 itemheight = max( itemheight,
1082 max( txtheight + 2, menucharsize.cy + 4));
1084 if (hfontOld) SelectObject (hdc, hfontOld);
1085 } else if( menuBar) {
1086 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088 lpitem->rect.bottom += itemheight;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1097 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1100 return lppop->cyMax;
1101 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1115 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117 lppop->Width = lppop->Height = 0;
1118 if (lppop->nItems == 0) return;
1121 SelectObject( hdc, get_menu_font(FALSE));
1126 lppop->maxBmpSize.cx = 0;
1127 lppop->maxBmpSize.cy = 0;
1129 while (start < lppop->nItems)
1131 lpitem = &lppop->items[start];
1133 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1134 orgX += MENU_COL_SPACE;
1135 orgY = MENU_TOP_MARGIN;
1137 maxTab = maxTabWidth = 0;
1138 /* Parse items until column break or end of menu */
1139 for (i = start; i < lppop->nItems; i++, lpitem++)
1142 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1145 maxX = max( maxX, lpitem->rect.right );
1146 orgY = lpitem->rect.bottom;
1147 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149 maxTab = max( maxTab, lpitem->xTab );
1150 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX = max( maxX, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = maxX;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->Height = max( lppop->Height, orgY );
1166 lppop->Width = maxX;
1168 /* space for 3d border */
1169 lppop->Height += MENU_BOTTOM_MARGIN;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight = MENU_GetMaxPopupHeight(lppop);
1174 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1175 if (lppop->Height >= maxHeight)
1177 lppop->Height = maxHeight;
1178 lppop->bScrolling = TRUE;
1182 lppop->bScrolling = FALSE;
1185 ReleaseDC( 0, hdc );
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1199 LPPOPUPMENU lppop, HWND hwndOwner )
1202 int start, i, orgX, orgY, maxY, helpPos;
1204 if ((lprect == NULL) || (lppop == NULL)) return;
1205 if (lppop->nItems == 0) return;
1206 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1207 lppop->Width = lprect->right - lprect->left;
1209 maxY = lprect->top+1;
1212 lppop->maxBmpSize.cx = 0;
1213 lppop->maxBmpSize.cy = 0;
1214 while (start < lppop->nItems)
1216 lpitem = &lppop->items[start];
1217 orgX = lprect->left;
1220 /* Parse items until line break or end of menu */
1221 for (i = start; i < lppop->nItems; i++, lpitem++)
1223 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1225 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1228 debug_print_menuitem (" item: ", lpitem, "");
1229 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231 if (lpitem->rect.right > lprect->right)
1233 if (i != start) break;
1234 else lpitem->rect.right = lprect->right;
1236 maxY = max( maxY, lpitem->rect.bottom );
1237 orgX = lpitem->rect.right;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start < i) lppop->items[start++].rect.bottom = maxY;
1244 lprect->bottom = maxY;
1245 lppop->Height = lprect->bottom - lprect->top;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem = &lppop->items[lppop->nItems-1];
1250 orgY = lpitem->rect.top;
1251 orgX = lprect->right;
1252 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1253 if ( (helpPos==-1) || (helpPos>i) )
1255 if (lpitem->rect.top != orgY) break; /* Other line */
1256 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1257 lpitem->rect.left += orgX - lpitem->rect.right;
1258 lpitem->rect.right = orgX;
1259 orgX = lpitem->rect.left;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1270 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1272 HDC hdcMem = CreateCompatibleDC(hdc);
1273 HBITMAP hOrigBitmap;
1274 UINT arrow_bitmap_width, arrow_bitmap_height;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1279 arrow_bitmap_width = bmp.bmWidth;
1280 arrow_bitmap_height = bmp.bmHeight;
1283 if (lppop->nScrollPos)
1284 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1289 rect.right = lppop->Width;
1290 rect.bottom = arrow_bitmap_height;
1291 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1292 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1293 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1294 rect.top = lppop->Height - arrow_bitmap_height;
1295 rect.bottom = lppop->Height;
1296 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1297 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1298 SelectObject(hdcMem, get_down_arrow_bitmap());
1300 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1302 lppop->Height - arrow_bitmap_height,
1303 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1304 SelectObject(hdcMem, hOrigBitmap);
1309 /***********************************************************************
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1315 UINT arrow_bitmap_height)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1328 /***********************************************************************
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1334 UINT height, BOOL menuBar, UINT odaction )
1337 BOOL flat_menu = FALSE;
1339 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1340 POPUPMENU *menu = MENU_GetMenu(hmenu);
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1348 arrow_bitmap_width = bmp.bmWidth;
1349 arrow_bitmap_height = bmp.bmHeight;
1352 if (lpitem->fType & MF_SYSMENU)
1354 if( !IsIconic(hwnd) )
1355 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1359 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1360 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1364 if (lpitem->fState & MF_HILITE)
1366 if(menuBar && !flat_menu) {
1367 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1368 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1370 if(lpitem->fState & MF_GRAYED)
1371 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1374 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1379 if (lpitem->fState & MF_GRAYED)
1380 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1382 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1383 SetBkColor( hdc, GetSysColor( bkgnd ) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1387 rect = lpitem->rect;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390 if (lpitem->fType & MF_OWNERDRAW)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1403 dis.CtlType = ODT_MENU;
1405 dis.itemID = lpitem->wID;
1406 dis.itemData = lpitem->dwItemData;
1408 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1409 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1410 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1411 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis.hwndItem = (HWND)hmenu;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1417 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1418 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1419 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1420 /* Draw the popup-menu arrow */
1421 if (lpitem->fType & MF_POPUP)
1422 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1423 arrow_bitmap_height);
1427 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429 if (lpitem->fState & MF_HILITE)
1433 InflateRect (&rect, -1, -1);
1434 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1435 InflateRect (&rect, 1, 1);
1436 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1441 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1443 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1447 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449 SetBkMode( hdc, TRANSPARENT );
1451 /* vertical separator */
1452 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1457 rc.left -= MENU_COL_SPACE / 2 + 1;
1459 rc.bottom = height - 3;
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1479 rc.top = ( rc.top + rc.bottom) / 2;
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1516 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1524 INT y = rect.top + rect.bottom;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1589 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1779 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1780 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1782 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1783 if (menu->FocusedItem != NO_SELECTED_ITEM)
1785 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1786 menu->FocusedItem = NO_SELECTED_ITEM;
1789 /* store the owner for DrawItem */
1790 menu->hwndOwner = hwndOwner;
1792 menu->nScrollPos = 0;
1793 MENU_PopupMenuCalcSize( menu, hwndOwner );
1795 /* adjust popup menu pos so that it fits within the desktop */
1797 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1798 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1800 /* FIXME: should use item rect */
1803 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1804 info.cbSize = sizeof(info);
1805 GetMonitorInfoW( monitor, &info );
1806 if( x + width > info.rcWork.right)
1808 if( xanchor && x >= width - xanchor )
1809 x -= width - xanchor;
1811 if( x + width > info.rcWork.right)
1812 x = info.rcWork.right - width;
1814 if( x < info.rcWork.left ) x = info.rcWork.left;
1816 if( y + height > info.rcWork.bottom)
1818 if( yanchor && y >= height + yanchor )
1819 y -= height + yanchor;
1821 if( y + height > info.rcWork.bottom)
1822 y = info.rcWork.bottom - height;
1824 if( y < info.rcWork.top ) y = info.rcWork.top;
1826 /* NOTE: In Windows, top menu popup is not owned. */
1827 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1828 WS_POPUP, x, y, width, height,
1829 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1831 if( !menu->hWnd ) return FALSE;
1832 if (!top_popup) top_popup = menu->hWnd;
1834 /* Display the window */
1836 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1837 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1838 UpdateWindow( menu->hWnd );
1843 /***********************************************************************
1844 * MENU_EnsureMenuItemVisible
1847 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1849 if (lppop->bScrolling)
1851 MENUITEM *item = &lppop->items[wIndex];
1852 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1853 UINT nOldPos = lppop->nScrollPos;
1855 UINT arrow_bitmap_height;
1858 GetClientRect(lppop->hWnd, &rc);
1860 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1861 arrow_bitmap_height = bmp.bmHeight;
1863 rc.top += arrow_bitmap_height;
1864 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1866 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1867 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1870 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1871 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1872 MENU_DrawScrollArrows(lppop, hdc);
1874 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1876 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1877 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1878 MENU_DrawScrollArrows(lppop, hdc);
1884 /***********************************************************************
1887 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1888 BOOL sendMenuSelect, HMENU topmenu )
1893 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1895 lppop = MENU_GetMenu( hmenu );
1896 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1898 if (lppop->FocusedItem == wIndex) return;
1899 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1900 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1901 if (!top_popup) top_popup = lppop->hWnd;
1903 SelectObject( hdc, get_menu_font(FALSE));
1905 /* Clear previous highlighted item */
1906 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1908 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1909 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1910 lppop->Height, !(lppop->wFlags & MF_POPUP),
1914 /* Highlight new item (if any) */
1915 lppop->FocusedItem = wIndex;
1916 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1918 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1919 lppop->items[wIndex].fState |= MF_HILITE;
1920 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1921 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1922 &lppop->items[wIndex], lppop->Height,
1923 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1927 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1928 SendMessageW( hwndOwner, WM_MENUSELECT,
1929 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1930 ip->fType | ip->fState |
1931 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1934 else if (sendMenuSelect) {
1937 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1938 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1939 MENUITEM *ip = &ptm->items[pos];
1940 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1941 ip->fType | ip->fState |
1942 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1946 ReleaseDC( lppop->hWnd, hdc );
1950 /***********************************************************************
1951 * MENU_MoveSelection
1953 * Moves currently selected item according to the offset parameter.
1954 * If there is no selection then it should select the last item if
1955 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1957 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1962 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1964 menu = MENU_GetMenu( hmenu );
1965 if ((!menu) || (!menu->items)) return;
1967 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1969 if( menu->nItems == 1 ) return; else
1970 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1972 if (!(menu->items[i].fType & MF_SEPARATOR))
1974 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1979 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1980 i >= 0 && i < menu->nItems ; i += offset)
1981 if (!(menu->items[i].fType & MF_SEPARATOR))
1983 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1989 /**********************************************************************
1992 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1995 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1998 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1999 TRACE("flags=%x str=%p\n", flags, str);
2001 if (IS_STRING_ITEM(flags))
2003 LPWSTR prevText = item->text;
2006 flags |= MF_SEPARATOR;
2012 /* Item beginning with a backspace is a help item */
2018 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2020 strcpyW( text, str );
2023 item->hbmpItem = NULL;
2024 HeapFree( GetProcessHeap(), 0, prevText );
2026 else if(( flags & MFT_BITMAP)) {
2027 item->hbmpItem = HBITMAP_32(LOWORD(str));
2028 /* setting bitmap clears text */
2029 HeapFree( GetProcessHeap(), 0, item->text );
2033 if (flags & MF_OWNERDRAW)
2034 item->dwItemData = (DWORD_PTR)str;
2036 item->dwItemData = 0;
2038 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2039 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2041 if (flags & MF_POPUP)
2043 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2044 if (menu) menu->wFlags |= MF_POPUP;
2056 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2058 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2059 flags |= MF_POPUP; /* keep popup */
2061 item->fType = flags & TYPE_MASK;
2062 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2063 for ModifyMenu, but Windows accepts it */
2064 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2066 /* Don't call SetRectEmpty here! */
2068 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2073 /**********************************************************************
2076 * Insert (allocate) a new item into a menu.
2078 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2083 if (!(menu = MENU_GetMenu(hMenu)))
2086 /* Find where to insert new item */
2088 if (flags & MF_BYPOSITION) {
2089 if (pos > menu->nItems)
2092 if (!MENU_FindItem( &hMenu, &pos, flags ))
2095 if (!(menu = MENU_GetMenu( hMenu )))
2100 /* Create new items array */
2102 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2105 WARN("allocation failed\n" );
2108 if (menu->nItems > 0)
2110 /* Copy the old array into the new one */
2111 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2112 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2113 (menu->nItems-pos)*sizeof(MENUITEM) );
2114 HeapFree( GetProcessHeap(), 0, menu->items );
2116 menu->items = newItems;
2118 memset( &newItems[pos], 0, sizeof(*newItems) );
2119 menu->Height = 0; /* force size recalculate */
2120 return &newItems[pos];
2124 /**********************************************************************
2125 * MENU_ParseResource
2127 * Parse a standard menu resource and add items to the menu.
2128 * Return a pointer to the end of the resource.
2130 * NOTE: flags is equivalent to the mtOption field
2132 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2140 flags = GET_WORD(res);
2141 end_flag = flags & MF_END;
2142 /* Remove MF_END because it has the same value as MF_HILITE */
2144 res += sizeof(WORD);
2145 if (!(flags & MF_POPUP))
2148 res += sizeof(WORD);
2151 if (!unicode) res += strlen(str) + 1;
2152 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2153 if (flags & MF_POPUP)
2155 HMENU hSubMenu = CreatePopupMenu();
2156 if (!hSubMenu) return NULL;
2157 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2159 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2160 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2162 else /* Not a popup */
2164 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2165 else AppendMenuW( hMenu, flags, id,
2166 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2168 } while (!end_flag);
2173 /**********************************************************************
2174 * MENUEX_ParseResource
2176 * Parse an extended menu resource and add items to the menu.
2177 * Return a pointer to the end of the resource.
2179 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2185 mii.cbSize = sizeof(mii);
2186 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2187 mii.fType = GET_DWORD(res);
2188 res += sizeof(DWORD);
2189 mii.fState = GET_DWORD(res);
2190 res += sizeof(DWORD);
2191 mii.wID = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2194 res += sizeof(WORD);
2195 /* Align the text on a word boundary. */
2196 res += (~((UINT_PTR)res - 1)) & 1;
2197 mii.dwTypeData = (LPWSTR) res;
2198 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2199 /* Align the following fields on a dword boundary. */
2200 res += (~((UINT_PTR)res - 1)) & 3;
2202 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2203 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2205 if (resinfo & 1) { /* Pop-up? */
2206 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2207 res += sizeof(DWORD);
2208 mii.hSubMenu = CreatePopupMenu();
2211 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2212 DestroyMenu(mii.hSubMenu);
2215 mii.fMask |= MIIM_SUBMENU;
2216 mii.fType |= MF_POPUP;
2218 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2220 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2221 mii.wID, mii.fType);
2222 mii.fType |= MF_SEPARATOR;
2224 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2225 } while (!(resinfo & MF_END));
2230 /***********************************************************************
2233 * Return the handle of the selected sub-popup menu (if any).
2235 static HMENU MENU_GetSubPopup( HMENU hmenu )
2240 menu = MENU_GetMenu( hmenu );
2242 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2244 item = &menu->items[menu->FocusedItem];
2245 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2246 return item->hSubMenu;
2251 /***********************************************************************
2252 * MENU_HideSubPopups
2254 * Hide the sub-popup menus of this menu.
2256 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2257 BOOL sendMenuSelect )
2259 POPUPMENU *menu = MENU_GetMenu( hmenu );
2261 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2263 if (menu && top_popup)
2269 if (menu->FocusedItem != NO_SELECTED_ITEM)
2271 item = &menu->items[menu->FocusedItem];
2272 if (!(item->fType & MF_POPUP) ||
2273 !(item->fState & MF_MOUSESELECT)) return;
2274 item->fState &= ~MF_MOUSESELECT;
2275 hsubmenu = item->hSubMenu;
2278 submenu = MENU_GetMenu( hsubmenu );
2279 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2280 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2281 DestroyWindow( submenu->hWnd );
2287 /***********************************************************************
2290 * Display the sub-menu of the selected item of this menu.
2291 * Return the handle of the submenu, or hmenu if no submenu to display.
2293 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2294 BOOL selectFirst, UINT wFlags )
2301 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2303 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2305 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2307 item = &menu->items[menu->FocusedItem];
2308 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2311 /* message must be sent before using item,
2312 because nearly everything may be changed by the application ! */
2314 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2315 if (!(wFlags & TPM_NONOTIFY))
2316 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2317 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2319 item = &menu->items[menu->FocusedItem];
2322 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2323 if (!(item->fState & MF_HILITE))
2325 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2326 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2328 SelectObject( hdc, get_menu_font(FALSE));
2330 item->fState |= MF_HILITE;
2331 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2332 ReleaseDC( menu->hWnd, hdc );
2334 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2337 item->fState |= MF_MOUSESELECT;
2339 if (IS_SYSTEM_MENU(menu))
2341 MENU_InitSysMenuPopup(item->hSubMenu,
2342 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2343 GetClassLongW( menu->hWnd, GCL_STYLE));
2345 NC_GetSysPopupPos( menu->hWnd, &rect );
2346 rect.top = rect.bottom;
2347 rect.right = GetSystemMetrics(SM_CXSIZE);
2348 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2352 GetWindowRect( menu->hWnd, &rect );
2353 if (menu->wFlags & MF_POPUP)
2355 RECT rc = item->rect;
2357 MENU_AdjustMenuItemRect(menu, &rc);
2359 /* The first item in the popup menu has to be at the
2360 same y position as the focused menu item */
2361 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2362 rect.top += rc.top - MENU_TOP_MARGIN;
2363 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2364 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2365 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2369 rect.left += item->rect.left;
2370 rect.top += item->rect.bottom;
2371 rect.right = item->rect.right - item->rect.left;
2372 rect.bottom = item->rect.bottom - item->rect.top;
2376 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2377 rect.left, rect.top, rect.right, rect.bottom );
2379 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2380 return item->hSubMenu;
2385 /**********************************************************************
2388 HWND MENU_IsMenuActive(void)
2393 /***********************************************************************
2396 * Walks menu chain trying to find a menu pt maps to.
2398 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2400 POPUPMENU *menu = MENU_GetMenu( hMenu );
2401 UINT item = menu->FocusedItem;
2404 /* try subpopup first (if any) */
2405 ret = (item != NO_SELECTED_ITEM &&
2406 (menu->items[item].fType & MF_POPUP) &&
2407 (menu->items[item].fState & MF_MOUSESELECT))
2408 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2410 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2412 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2413 if( menu->wFlags & MF_POPUP )
2415 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2417 else if (ht == HTSYSMENU)
2418 ret = get_win_sys_menu( menu->hWnd );
2419 else if (ht == HTMENU)
2420 ret = GetMenu( menu->hWnd );
2425 /***********************************************************************
2426 * MENU_ExecFocusedItem
2428 * Execute a menu item (for instance when user pressed Enter).
2429 * Return the wID of the executed item. Otherwise, -1 indicating
2430 * that no menu item was executed, -2 if a popup is shown;
2431 * Have to receive the flags for the TrackPopupMenu options to avoid
2432 * sending unwanted message.
2435 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2438 POPUPMENU *menu = MENU_GetMenu( hMenu );
2440 TRACE("%p hmenu=%p\n", pmt, hMenu);
2442 if (!menu || !menu->nItems ||
2443 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2445 item = &menu->items[menu->FocusedItem];
2447 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2449 if (!(item->fType & MF_POPUP))
2451 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2453 /* If TPM_RETURNCMD is set you return the id, but
2454 do not send a message to the owner */
2455 if(!(wFlags & TPM_RETURNCMD))
2457 if( menu->wFlags & MF_SYSMENU )
2458 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2459 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2462 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2463 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2466 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2474 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2481 /***********************************************************************
2482 * MENU_SwitchTracking
2484 * Helper function for menu navigation routines.
2486 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2488 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2489 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2491 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2493 if( pmt->hTopMenu != hPtMenu &&
2494 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2496 /* both are top level menus (system and menu-bar) */
2497 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2498 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2499 pmt->hTopMenu = hPtMenu;
2501 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2502 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2506 /***********************************************************************
2509 * Return TRUE if we can go on with menu tracking.
2511 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2513 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2518 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2521 if( IS_SYSTEM_MENU(ptmenu) )
2522 item = ptmenu->items;
2524 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2528 if( ptmenu->FocusedItem != id )
2529 MENU_SwitchTracking( pmt, hPtMenu, id );
2531 /* If the popup menu is not already "popped" */
2532 if(!(item->fState & MF_MOUSESELECT ))
2534 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2539 /* Else the click was on the menu bar, finish the tracking */
2544 /***********************************************************************
2547 * Return the value of MENU_ExecFocusedItem if
2548 * the selected item was not a popup. Else open the popup.
2549 * A -1 return value indicates that we go on with menu tracking.
2552 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2554 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2559 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2562 if( IS_SYSTEM_MENU(ptmenu) )
2563 item = ptmenu->items;
2565 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2567 if( item && (ptmenu->FocusedItem == id ))
2569 if( !(item->fType & MF_POPUP) )
2571 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2572 return (executedMenuId < 0) ? -1 : executedMenuId;
2575 /* If we are dealing with the top-level menu */
2576 /* and this is a click on an already "popped" item: */
2577 /* Stop the menu tracking and close the opened submenus */
2578 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2581 ptmenu->bTimeToHide = TRUE;
2587 /***********************************************************************
2590 * Return TRUE if we can go on with menu tracking.
2592 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2594 UINT id = NO_SELECTED_ITEM;
2595 POPUPMENU *ptmenu = NULL;
2599 ptmenu = MENU_GetMenu( hPtMenu );
2600 if( IS_SYSTEM_MENU(ptmenu) )
2603 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2606 if( id == NO_SELECTED_ITEM )
2608 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2609 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2612 else if( ptmenu->FocusedItem != id )
2614 MENU_SwitchTracking( pmt, hPtMenu, id );
2615 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2621 /***********************************************************************
2624 static void MENU_SetCapture( HWND hwnd )
2628 SERVER_START_REQ( set_capture_window )
2631 req->flags = CAPTURE_MENU;
2632 if (!wine_server_call_err( req ))
2634 previous = reply->previous;
2635 hwnd = reply->full_handle;
2640 if (previous && previous != hwnd)
2641 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2645 /***********************************************************************
2648 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2650 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2652 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2654 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2655 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2657 MDINEXTMENU next_menu;
2662 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2663 next_menu.hmenuNext = 0;
2664 next_menu.hwndNext = 0;
2665 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2667 TRACE("%p [%p] -> %p [%p]\n",
2668 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2670 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2672 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2673 hNewWnd = pmt->hOwnerWnd;
2674 if( IS_SYSTEM_MENU(menu) )
2676 /* switch to the menu bar */
2678 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2682 menu = MENU_GetMenu( hNewMenu );
2683 id = menu->nItems - 1;
2686 else if (style & WS_SYSMENU )
2688 /* switch to the system menu */
2689 hNewMenu = get_win_sys_menu( hNewWnd );
2693 else /* application returned a new menu to switch to */
2695 hNewMenu = next_menu.hmenuNext;
2696 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2698 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2700 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2702 if (style & WS_SYSMENU &&
2703 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2705 /* get the real system menu */
2706 hNewMenu = get_win_sys_menu(hNewWnd);
2708 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2710 /* FIXME: Not sure what to do here;
2711 * perhaps try to track hNewMenu as a popup? */
2713 TRACE(" -- got confused.\n");
2720 if( hNewMenu != pmt->hTopMenu )
2722 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2724 if( pmt->hCurrentMenu != pmt->hTopMenu )
2725 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2728 if( hNewWnd != pmt->hOwnerWnd )
2730 pmt->hOwnerWnd = hNewWnd;
2731 MENU_SetCapture( pmt->hOwnerWnd );
2734 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2735 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2742 /***********************************************************************
2745 * The idea is not to show the popup if the next input message is
2746 * going to hide it anyway.
2748 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2752 msg.hwnd = pmt->hOwnerWnd;
2754 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2755 pmt->trackFlags |= TF_SKIPREMOVE;
2760 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2761 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2763 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2764 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2765 if( msg.message == WM_KEYDOWN &&
2766 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2768 pmt->trackFlags |= TF_SUSPENDPOPUP;
2775 /* failures go through this */
2776 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2780 /***********************************************************************
2783 * Handle a VK_ESCAPE key event in a menu.
2785 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2787 BOOL bEndMenu = TRUE;
2789 if (pmt->hCurrentMenu != pmt->hTopMenu)
2791 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2793 if (menu->wFlags & MF_POPUP)
2795 HMENU hmenutmp, hmenuprev;
2797 hmenuprev = hmenutmp = pmt->hTopMenu;
2799 /* close topmost popup */
2800 while (hmenutmp != pmt->hCurrentMenu)
2802 hmenuprev = hmenutmp;
2803 hmenutmp = MENU_GetSubPopup( hmenuprev );
2806 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2807 pmt->hCurrentMenu = hmenuprev;
2815 /***********************************************************************
2818 * Handle a VK_LEFT key event in a menu.
2820 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2823 HMENU hmenutmp, hmenuprev;
2826 hmenuprev = hmenutmp = pmt->hTopMenu;
2827 menu = MENU_GetMenu( hmenutmp );
2829 /* Try to move 1 column left (if possible) */
2830 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2831 NO_SELECTED_ITEM ) {
2833 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2838 /* close topmost popup */
2839 while (hmenutmp != pmt->hCurrentMenu)
2841 hmenuprev = hmenutmp;
2842 hmenutmp = MENU_GetSubPopup( hmenuprev );
2845 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2846 pmt->hCurrentMenu = hmenuprev;
2848 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2850 /* move menu bar selection if no more popups are left */
2852 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2853 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2855 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2857 /* A sublevel menu was displayed - display the next one
2858 * unless there is another displacement coming up */
2860 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2861 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2862 pmt->hTopMenu, TRUE, wFlags);
2868 /***********************************************************************
2871 * Handle a VK_RIGHT key event in a menu.
2873 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2876 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2879 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2881 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2882 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2884 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2886 /* If already displaying a popup, try to display sub-popup */
2888 hmenutmp = pmt->hCurrentMenu;
2889 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2891 /* if subpopup was displayed then we are done */
2892 if (hmenutmp != pmt->hCurrentMenu) return;
2895 /* Check to see if there's another column */
2896 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2897 NO_SELECTED_ITEM ) {
2898 TRACE("Going to %d.\n", nextcol );
2899 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2904 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2906 if( pmt->hCurrentMenu != pmt->hTopMenu )
2908 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2909 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2910 } else hmenutmp = 0;
2912 /* try to move to the next item */
2913 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2914 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2916 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2917 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2918 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2919 pmt->hTopMenu, TRUE, wFlags);
2923 /***********************************************************************
2926 * Menu tracking code.
2928 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2929 HWND hwnd, const RECT *lprect )
2934 INT executedMenuId = -1;
2936 BOOL enterIdleSent = FALSE;
2939 mt.hCurrentMenu = hmenu;
2940 mt.hTopMenu = hmenu;
2941 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2945 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2946 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2949 if (!(menu = MENU_GetMenu( hmenu )))
2951 WARN("Invalid menu handle %p\n", hmenu);
2952 SetLastError(ERROR_INVALID_MENU_HANDLE);
2956 if (wFlags & TPM_BUTTONDOWN)
2958 /* Get the result in order to start the tracking or not */
2959 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2960 fEndMenu = !fRemove;
2963 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2965 MENU_SetCapture( mt.hOwnerWnd );
2969 menu = MENU_GetMenu( mt.hCurrentMenu );
2970 if (!menu) /* sometimes happens if I do a window manager close */
2973 /* we have to keep the message in the queue until it's
2974 * clear that menu loop is not over yet. */
2978 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2980 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2981 /* remove the message from the queue */
2982 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2988 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2989 enterIdleSent = TRUE;
2990 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2996 /* check if EndMenu() tried to cancel us, by posting this message */
2997 if(msg.message == WM_CANCELMODE)
2999 /* we are now out of the loop */
3002 /* remove the message from the queue */
3003 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3005 /* break out of internal loop, ala ESCAPE */
3009 TranslateMessage( &msg );
3012 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3013 enterIdleSent=FALSE;
3016 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3019 * Use the mouse coordinates in lParam instead of those in the MSG
3020 * struct to properly handle synthetic messages. They are already
3021 * in screen coordinates.
3023 mt.pt.x = (short)LOWORD(msg.lParam);
3024 mt.pt.y = (short)HIWORD(msg.lParam);
3026 /* Find a menu for this mouse event */
3027 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3031 /* no WM_NC... messages in captured state */
3033 case WM_RBUTTONDBLCLK:
3034 case WM_RBUTTONDOWN:
3035 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3037 case WM_LBUTTONDBLCLK:
3038 case WM_LBUTTONDOWN:
3039 /* If the message belongs to the menu, removes it from the queue */
3040 /* Else, end menu tracking */
3041 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3042 fEndMenu = !fRemove;
3046 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3049 /* Check if a menu was selected by the mouse */
3052 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3054 /* End the loop if executedMenuId is an item ID */
3055 /* or if the job was done (executedMenuId = 0). */
3056 fEndMenu = fRemove = (executedMenuId != -1);
3058 /* No menu was selected by the mouse */
3059 /* if the function was called by TrackPopupMenu, continue
3060 with the menu tracking. If not, stop it */
3062 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3067 /* the selected menu item must be changed every time */
3068 /* the mouse moves. */
3071 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3073 } /* switch(msg.message) - mouse */
3075 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3077 fRemove = TRUE; /* Keyboard messages are always removed */
3090 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3091 NO_SELECTED_ITEM, FALSE, 0 );
3092 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3093 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3097 case VK_DOWN: /* If on menu bar, pull-down the menu */
3099 menu = MENU_GetMenu( mt.hCurrentMenu );
3100 if (!(menu->wFlags & MF_POPUP))
3101 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3102 else /* otherwise try to move selection */
3103 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3104 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3108 MENU_KeyLeft( &mt, wFlags );
3112 MENU_KeyRight( &mt, wFlags );
3116 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3122 hi.cbSize = sizeof(HELPINFO);
3123 hi.iContextType = HELPINFO_MENUITEM;
3124 if (menu->FocusedItem == NO_SELECTED_ITEM)
3127 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3128 hi.hItemHandle = hmenu;
3129 hi.dwContextId = menu->dwContextHelpID;
3130 hi.MousePos = msg.pt;
3131 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3138 break; /* WM_KEYDOWN */
3145 if (msg.wParam == '\r' || msg.wParam == ' ')
3147 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3148 fEndMenu = (executedMenuId != -2);
3153 /* Hack to avoid control chars. */
3154 /* We will find a better way real soon... */
3155 if (msg.wParam < 32) break;
3157 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3158 LOWORD(msg.wParam), FALSE );
3159 if (pos == (UINT)-2) fEndMenu = TRUE;
3160 else if (pos == (UINT)-1) MessageBeep(0);
3163 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3165 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3166 fEndMenu = (executedMenuId != -2);
3170 } /* switch(msg.message) - kbd */
3174 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3175 DispatchMessageW( &msg );
3179 if (!fEndMenu) fRemove = TRUE;
3181 /* finally remove message from the queue */
3183 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3184 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3185 else mt.trackFlags &= ~TF_SKIPREMOVE;
3188 MENU_SetCapture(0); /* release the capture */
3190 /* If dropdown is still painted and the close box is clicked on
3191 then the menu will be destroyed as part of the DispatchMessage above.
3192 This will then invalidate the menu handle in mt.hTopMenu. We should
3193 check for this first. */
3194 if( IsMenu( mt.hTopMenu ) )
3196 menu = MENU_GetMenu( mt.hTopMenu );
3198 if( IsWindow( mt.hOwnerWnd ) )
3200 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3202 if (menu && (menu->wFlags & MF_POPUP))
3204 DestroyWindow( menu->hWnd );
3207 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3208 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3211 /* Reset the variable for hiding menu */
3212 if( menu ) menu->bTimeToHide = FALSE;
3215 /* The return value is only used by TrackPopupMenu */
3216 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3217 if (executedMenuId < 0) executedMenuId = 0;
3218 return executedMenuId;
3221 /***********************************************************************
3224 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3228 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3232 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3233 if (!(wFlags & TPM_NONOTIFY))
3234 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3236 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3238 if (!(wFlags & TPM_NONOTIFY))
3240 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3241 /* If an app changed/recreated menu bar entries in WM_INITMENU
3242 * menu sizes will be recalculated once the menu created/shown.
3246 /* This makes the menus of applications built with Delphi work.
3247 * It also enables menus to be displayed in more than one window,
3248 * but there are some bugs left that need to be fixed in this case.
3250 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3254 /***********************************************************************
3257 static BOOL MENU_ExitTracking(HWND hWnd)
3259 TRACE("hwnd=%p\n", hWnd);
3261 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3267 /***********************************************************************
3268 * MENU_TrackMouseMenuBar
3270 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3272 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3274 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3275 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3277 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3281 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3282 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3283 MENU_ExitTracking(hWnd);
3288 /***********************************************************************
3289 * MENU_TrackKbdMenuBar
3291 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3293 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3295 UINT uItem = NO_SELECTED_ITEM;
3297 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3299 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3301 /* find window that has a menu */
3303 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3304 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3306 /* check if we have to track a system menu */
3308 hTrackMenu = GetMenu( hwnd );
3309 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3311 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3312 hTrackMenu = get_win_sys_menu( hwnd );
3314 wParam |= HTSYSMENU; /* prevent item lookup */
3317 if (!IsMenu( hTrackMenu )) return;
3319 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3321 if( wChar && wChar != ' ' )
3323 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3324 if ( uItem >= (UINT)(-2) )
3326 if( uItem == (UINT)(-1) ) MessageBeep(0);
3327 /* schedule end of menu tracking */
3328 wFlags |= TF_ENDMENU;
3333 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3335 if (wParam & HTSYSMENU)
3337 /* prevent sysmenu activation for managed windows on Alt down/up */
3338 if (GetPropA( hwnd, "__wine_x11_managed" ))
3339 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3343 if( uItem == NO_SELECTED_ITEM )
3344 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3346 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3350 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3351 MENU_ExitTracking( hwnd );
3355 /**********************************************************************
3356 * TrackPopupMenu (USER32.@)
3358 * Like the win32 API, the function return the command ID only if the
3359 * flag TPM_RETURNCMD is on.
3362 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3363 INT nReserved, HWND hWnd, const RECT *lpRect )
3367 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3369 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3370 if (!(wFlags & TPM_NONOTIFY))
3371 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3373 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3374 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3375 MENU_ExitTracking(hWnd);
3380 /**********************************************************************
3381 * TrackPopupMenuEx (USER32.@)
3383 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3384 HWND hWnd, LPTPMPARAMS lpTpm )
3386 FIXME("not fully implemented\n" );
3387 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3388 lpTpm ? &lpTpm->rcExclude : NULL );
3391 /***********************************************************************
3394 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3396 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3398 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3404 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3405 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3409 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3410 return MA_NOACTIVATE;
3415 BeginPaint( hwnd, &ps );
3416 MENU_DrawPopupMenu( hwnd, ps.hdc,
3417 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3418 EndPaint( hwnd, &ps );
3425 /* zero out global pointer in case resident popup window was destroyed. */
3426 if (hwnd == top_popup) top_popup = 0;
3433 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3436 SetWindowLongPtrW( hwnd, 0, 0 );
3439 case MM_SETMENUHANDLE:
3440 SetWindowLongPtrW( hwnd, 0, wParam );
3443 case MM_GETMENUHANDLE:
3444 return GetWindowLongPtrW( hwnd, 0 );
3447 return DefWindowProcW( hwnd, message, wParam, lParam );
3453 /***********************************************************************
3454 * MENU_GetMenuBarHeight
3456 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3458 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3459 INT orgX, INT orgY )
3465 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3467 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3469 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3470 SelectObject( hdc, get_menu_font(FALSE));
3471 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3472 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3473 ReleaseDC( hwnd, hdc );
3474 return lppop->Height;
3478 /*******************************************************************
3479 * ChangeMenuA (USER32.@)
3481 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3482 UINT id, UINT flags )
3484 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3485 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3487 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3488 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3490 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3491 flags & MF_BYPOSITION ? pos : id,
3492 flags & ~MF_REMOVE );
3493 /* Default: MF_INSERT */
3494 return InsertMenuA( hMenu, pos, flags, id, data );
3498 /*******************************************************************
3499 * ChangeMenuW (USER32.@)
3501 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3502 UINT id, UINT flags )
3504 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3505 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3507 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3508 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3510 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3511 flags & MF_BYPOSITION ? pos : id,
3512 flags & ~MF_REMOVE );
3513 /* Default: MF_INSERT */
3514 return InsertMenuW( hMenu, pos, flags, id, data );
3518 /*******************************************************************
3519 * CheckMenuItem (USER32.@)
3521 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3526 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3527 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3528 ret = item->fState & MF_CHECKED;
3529 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3530 else item->fState &= ~MF_CHECKED;
3535 /**********************************************************************
3536 * EnableMenuItem (USER32.@)
3538 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3544 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3546 /* Get the Popupmenu to access the owner menu */
3547 if (!(menu = MENU_GetMenu(hMenu)))
3550 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3553 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3554 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3556 /* If the close item in the system menu change update the close button */
3557 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3559 if (menu->hSysMenuOwner != 0)
3562 POPUPMENU* parentMenu;
3564 /* Get the parent menu to access*/
3565 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3568 /* Refresh the frame to reflect the change */
3569 GetWindowRect(parentMenu->hWnd, &rc);
3570 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3572 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3580 /*******************************************************************
3581 * GetMenuStringA (USER32.@)
3583 INT WINAPI GetMenuStringA(
3584 HMENU hMenu, /* [in] menuhandle */
3585 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3586 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3587 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3588 UINT wFlags /* [in] MF_ flags */
3592 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3593 if (str && nMaxSiz) str[0] = '\0';
3594 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3595 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3598 if (!item->text) return 0;
3599 if (!str || !nMaxSiz) return strlenW(item->text);
3600 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3602 TRACE("returning '%s'\n", str );
3607 /*******************************************************************
3608 * GetMenuStringW (USER32.@)
3610 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3611 LPWSTR str, INT nMaxSiz, UINT wFlags )
3615 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3616 if (str && nMaxSiz) str[0] = '\0';
3617 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3618 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3621 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3622 if( !(item->text)) {
3626 lstrcpynW( str, item->text, nMaxSiz );
3627 return strlenW(str);
3631 /**********************************************************************
3632 * HiliteMenuItem (USER32.@)
3634 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3638 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3639 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3640 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3641 if (menu->FocusedItem == wItemID) return TRUE;
3642 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3643 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3648 /**********************************************************************
3649 * GetMenuState (USER32.@)
3651 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3654 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3655 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3656 debug_print_menuitem (" item: ", item, "");
3657 if (item->fType & MF_POPUP)
3659 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3660 if (!menu) return -1;
3661 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3665 /* We used to (from way back then) mask the result to 0xff. */
3666 /* I don't know why and it seems wrong as the documented */
3667 /* return flag MF_SEPARATOR is outside that mask. */
3668 return (item->fType | item->fState);
3673 /**********************************************************************
3674 * GetMenuItemCount (USER32.@)
3676 INT WINAPI GetMenuItemCount( HMENU hMenu )
3678 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3679 if (!menu) return -1;
3680 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3681 return menu->nItems;
3685 /**********************************************************************
3686 * GetMenuItemID (USER32.@)
3688 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3692 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3693 if (lpmi->fType & MF_POPUP) return -1;
3699 /*******************************************************************
3700 * InsertMenuW (USER32.@)
3702 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3703 UINT_PTR id, LPCWSTR str )
3707 if (IS_STRING_ITEM(flags) && str)
3708 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3709 hMenu, pos, flags, id, debugstr_w(str) );
3710 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3711 hMenu, pos, flags, id, str );
3713 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3715 if (!(MENU_SetItemData( item, flags, id, str )))
3717 RemoveMenu( hMenu, pos, flags );
3721 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3722 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3724 item->hCheckBit = item->hUnCheckBit = 0;
3729 /*******************************************************************
3730 * InsertMenuA (USER32.@)
3732 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3733 UINT_PTR id, LPCSTR str )
3737 if (IS_STRING_ITEM(flags) && str)
3739 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3740 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3743 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3744 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3745 HeapFree( GetProcessHeap(), 0, newstr );
3749 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3753 /*******************************************************************
3754 * AppendMenuA (USER32.@)
3756 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3757 UINT_PTR id, LPCSTR data )
3759 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3763 /*******************************************************************
3764 * AppendMenuW (USER32.@)
3766 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3767 UINT_PTR id, LPCWSTR data )
3769 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3773 /**********************************************************************
3774 * RemoveMenu (USER32.@)
3776 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3781 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3782 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3783 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3787 MENU_FreeItemData( item );
3789 if (--menu->nItems == 0)
3791 HeapFree( GetProcessHeap(), 0, menu->items );
3796 while(nPos < menu->nItems)
3802 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3803 menu->nItems * sizeof(MENUITEM) );
3809 /**********************************************************************
3810 * DeleteMenu (USER32.@)
3812 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3814 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3815 if (!item) return FALSE;
3816 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3817 /* nPos is now the position of the item */
3818 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3823 /*******************************************************************
3824 * ModifyMenuW (USER32.@)
3826 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3827 UINT_PTR id, LPCWSTR str )
3831 if (IS_STRING_ITEM(flags))
3832 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3834 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3836 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3837 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3838 return MENU_SetItemData( item, flags, id, str );
3842 /*******************************************************************
3843 * ModifyMenuA (USER32.@)
3845 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3846 UINT_PTR id, LPCSTR str )
3850 if (IS_STRING_ITEM(flags) && str)
3852 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3853 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3856 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3857 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3858 HeapFree( GetProcessHeap(), 0, newstr );
3862 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3866 /**********************************************************************
3867 * CreatePopupMenu (USER32.@)
3869 HMENU WINAPI CreatePopupMenu(void)
3874 if (!(hmenu = CreateMenu())) return 0;
3875 menu = MENU_GetMenu( hmenu );
3876 menu->wFlags |= MF_POPUP;
3877 menu->bTimeToHide = FALSE;
3882 /**********************************************************************
3883 * GetMenuCheckMarkDimensions (USER.417)
3884 * GetMenuCheckMarkDimensions (USER32.@)
3886 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3888 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3892 /**********************************************************************
3893 * SetMenuItemBitmaps (USER32.@)
3895 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3896 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3899 TRACE("(%p, %04x, %04x, %p, %p)\n",
3900 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3901 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3903 if (!hNewCheck && !hNewUnCheck)
3905 item->fState &= ~MF_USECHECKBITMAPS;
3907 else /* Install new bitmaps */
3909 item->hCheckBit = hNewCheck;
3910 item->hUnCheckBit = hNewUnCheck;
3911 item->fState |= MF_USECHECKBITMAPS;
3917 /**********************************************************************
3918 * CreateMenu (USER32.@)
3920 HMENU WINAPI CreateMenu(void)
3924 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3925 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3927 ZeroMemory(menu, sizeof(POPUPMENU));
3928 menu->wMagic = MENU_MAGIC;
3929 menu->FocusedItem = NO_SELECTED_ITEM;
3930 menu->bTimeToHide = FALSE;
3932 TRACE("return %p\n", hMenu );
3938 /**********************************************************************
3939 * DestroyMenu (USER32.@)
3941 BOOL WINAPI DestroyMenu( HMENU hMenu )
3943 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3945 TRACE("(%p)\n", hMenu);
3948 if (!lppop) return FALSE;
3950 lppop->wMagic = 0; /* Mark it as destroyed */
3952 /* DestroyMenu should not destroy system menu popup owner */
3953 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3955 DestroyWindow( lppop->hWnd );
3959 if (lppop->items) /* recursively destroy submenus */
3962 MENUITEM *item = lppop->items;
3963 for (i = lppop->nItems; i > 0; i--, item++)
3965 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3966 MENU_FreeItemData( item );
3968 HeapFree( GetProcessHeap(), 0, lppop->items );
3970 USER_HEAP_FREE( hMenu );
3975 /**********************************************************************
3976 * GetSystemMenu (USER32.@)
3978 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3980 WND *wndPtr = WIN_GetPtr( hWnd );
3983 if (wndPtr == WND_DESKTOP) return 0;
3984 if (wndPtr == WND_OTHER_PROCESS)
3986 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3990 if (wndPtr->hSysMenu && bRevert)
3992 DestroyMenu(wndPtr->hSysMenu);
3993 wndPtr->hSysMenu = 0;
3996 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3997 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3999 if( wndPtr->hSysMenu )
4002 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4004 /* Store the dummy sysmenu handle to facilitate the refresh */
4005 /* of the close button if the SC_CLOSE item change */
4006 menu = MENU_GetMenu(retvalue);
4008 menu->hSysMenuOwner = wndPtr->hSysMenu;
4010 WIN_ReleasePtr( wndPtr );
4012 return bRevert ? 0 : retvalue;
4016 /*******************************************************************
4017 * SetSystemMenu (USER32.@)
4019 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4021 WND *wndPtr = WIN_GetPtr( hwnd );
4023 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4025 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4026 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4027 WIN_ReleasePtr( wndPtr );
4034 /**********************************************************************
4035 * GetMenu (USER32.@)
4037 HMENU WINAPI GetMenu( HWND hWnd )
4039 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4040 TRACE("for %p returning %p\n", hWnd, retvalue);
4044 /**********************************************************************
4045 * GetMenuBarInfo (USER32.@)
4047 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4049 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4053 /**********************************************************************
4056 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4057 * SetWindowPos call that would result if SetMenu were called directly.
4059 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4061 TRACE("(%p, %p);\n", hWnd, hMenu);
4063 if (hMenu && !IsMenu(hMenu))
4065 WARN("hMenu %p is not a menu handle\n", hMenu);
4068 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4071 hWnd = WIN_GetFullHandle( hWnd );
4072 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4078 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4080 lpmenu->hWnd = hWnd;
4081 lpmenu->Height = 0; /* Make sure we recalculate the size */
4083 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4088 /**********************************************************************
4089 * SetMenu (USER32.@)
4091 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4093 if(!MENU_SetMenu(hWnd, hMenu))
4096 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4097 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4102 /**********************************************************************
4103 * GetSubMenu (USER32.@)
4105 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4109 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4110 if (!(lpmi->fType & MF_POPUP)) return 0;
4111 return lpmi->hSubMenu;
4115 /**********************************************************************
4116 * DrawMenuBar (USER32.@)
4118 BOOL WINAPI DrawMenuBar( HWND hWnd )
4121 HMENU hMenu = GetMenu(hWnd);
4123 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4125 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4127 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4128 lppop->hwndOwner = hWnd;
4129 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4130 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4134 /***********************************************************************
4135 * DrawMenuBarTemp (USER32.@)
4139 * called by W98SE desk.cpl Control Panel Applet
4141 * Not 100% sure about the param names, but close.
4143 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4148 BOOL flat_menu = FALSE;
4150 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4153 hMenu = GetMenu(hwnd);
4156 hFont = get_menu_font(FALSE);
4158 lppop = MENU_GetMenu( hMenu );
4159 if (lppop == NULL || lprect == NULL)
4161 retvalue = GetSystemMetrics(SM_CYMENU);
4165 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4167 hfontOld = SelectObject( hDC, hFont);
4169 if (lppop->Height == 0)
4170 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4172 lprect->bottom = lprect->top + lppop->Height;
4174 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4176 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4177 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4178 LineTo( hDC, lprect->right, lprect->bottom );
4180 if (lppop->nItems == 0)
4182 retvalue = GetSystemMetrics(SM_CYMENU);
4186 for (i = 0; i < lppop->nItems; i++)
4188 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4189 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4191 retvalue = lppop->Height;
4194 if (hfontOld) SelectObject (hDC, hfontOld);
4198 /***********************************************************************
4199 * EndMenu (USER.187)
4200 * EndMenu (USER32.@)
4202 void WINAPI EndMenu(void)
4204 /* if we are in the menu code, and it is active */
4205 if (!fEndMenu && top_popup)
4207 /* terminate the menu handling code */
4210 /* needs to be posted to wakeup the internal menu handler */
4211 /* which will now terminate the menu, in the event that */
4212 /* the main window was minimized, or lost focus, so we */
4213 /* don't end up with an orphaned menu */
4214 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4219 /***********************************************************************
4220 * LookupMenuHandle (USER.217)
4222 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4224 HMENU hmenu32 = HMENU_32(hmenu);
4226 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4227 else return HMENU_16(hmenu32);
4231 /**********************************************************************
4232 * LoadMenu (USER.150)
4234 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4240 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4241 if (!name) return 0;
4243 instance = GetExePtr( instance );
4244 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4245 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4246 hMenu = LoadMenuIndirect16(LockResource16(handle));
4247 FreeResource16( handle );
4252 /*****************************************************************
4253 * LoadMenuA (USER32.@)
4255 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4257 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4258 if (!hrsrc) return 0;
4259 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4263 /*****************************************************************
4264 * LoadMenuW (USER32.@)
4266 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4268 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4269 if (!hrsrc) return 0;
4270 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4274 /**********************************************************************
4275 * LoadMenuIndirect (USER.220)
4277 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4280 WORD version, offset;
4281 LPCSTR p = (LPCSTR)template;
4283 TRACE("(%p)\n", template );
4284 version = GET_WORD(p);
4288 WARN("version must be 0 for Win16\n" );
4291 offset = GET_WORD(p);
4292 p += sizeof(WORD) + offset;
4293 if (!(hMenu = CreateMenu())) return 0;
4294 if (!MENU_ParseResource( p, hMenu, FALSE ))
4296 DestroyMenu( hMenu );
4299 return HMENU_16(hMenu);
4303 /**********************************************************************
4304 * LoadMenuIndirectW (USER32.@)
4306 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4309 WORD version, offset;
4310 LPCSTR p = (LPCSTR)template;
4312 version = GET_WORD(p);
4314 TRACE("%p, ver %d\n", template, version );
4317 case 0: /* standard format is version of 0 */
4318 offset = GET_WORD(p);
4319 p += sizeof(WORD) + offset;
4320 if (!(hMenu = CreateMenu())) return 0;
4321 if (!MENU_ParseResource( p, hMenu, TRUE ))
4323 DestroyMenu( hMenu );
4327 case 1: /* extended format is version of 1 */
4328 offset = GET_WORD(p);
4329 p += sizeof(WORD) + offset;
4330 if (!(hMenu = CreateMenu())) return 0;
4331 if (!MENUEX_ParseResource( p, hMenu))
4333 DestroyMenu( hMenu );
4338 ERR("version %d not supported.\n", version);
4344 /**********************************************************************
4345 * LoadMenuIndirectA (USER32.@)
4347 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4349 return LoadMenuIndirectW( template );
4353 /**********************************************************************
4356 BOOL WINAPI IsMenu(HMENU hmenu)
4358 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4362 SetLastError(ERROR_INVALID_MENU_HANDLE);
4368 /**********************************************************************
4369 * GetMenuItemInfo_common
4372 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4373 LPMENUITEMINFOW lpmii, BOOL unicode)
4375 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4377 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4382 if( lpmii->fMask & MIIM_TYPE) {
4383 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4384 WARN("invalid combination of fMask bits used\n");
4385 /* this does not happen on Win9x/ME */
4386 SetLastError( ERROR_INVALID_PARAMETER);
4389 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4390 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4391 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4392 if( lpmii->fType & MFT_BITMAP) {
4393 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4395 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4396 /* this does not happen on Win9x/ME */
4397 lpmii->dwTypeData = 0;
4402 /* copy the text string */
4403 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4405 if(lpmii->dwTypeData && lpmii->cch) {
4408 *((WCHAR *)lpmii->dwTypeData) = 0;
4410 *((CHAR *)lpmii->dwTypeData) = 0;
4416 len = strlenW(menu->text);
4417 if(lpmii->dwTypeData && lpmii->cch)
4418 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4422 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4423 0, NULL, NULL ) - 1;
4424 if(lpmii->dwTypeData && lpmii->cch)
4425 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4426 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4427 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4429 /* if we've copied a substring we return its length */
4430 if(lpmii->dwTypeData && lpmii->cch)
4431 if (lpmii->cch <= len + 1)
4436 /* return length of string */
4437 /* not on Win9x/ME if fType & MFT_BITMAP */
4443 if (lpmii->fMask & MIIM_FTYPE)
4444 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4446 if (lpmii->fMask & MIIM_BITMAP)
4447 lpmii->hbmpItem = menu->hbmpItem;
4449 if (lpmii->fMask & MIIM_STATE)
4450 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4452 if (lpmii->fMask & MIIM_ID)
4453 lpmii->wID = menu->wID;
4455 if (lpmii->fMask & MIIM_SUBMENU)
4456 lpmii->hSubMenu = menu->hSubMenu;
4458 /* hSubMenu is always cleared
4459 * (not on Win9x/ME ) */
4460 lpmii->hSubMenu = 0;
4463 if (lpmii->fMask & MIIM_CHECKMARKS) {
4464 lpmii->hbmpChecked = menu->hCheckBit;
4465 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4467 if (lpmii->fMask & MIIM_DATA)
4468 lpmii->dwItemData = menu->dwItemData;
4473 /**********************************************************************
4474 * GetMenuItemInfoA (USER32.@)
4476 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4477 LPMENUITEMINFOA lpmii)
4481 if( lpmii->cbSize != sizeof( mii) &&
4482 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4483 SetLastError( ERROR_INVALID_PARAMETER);
4486 memcpy( &mii, lpmii, lpmii->cbSize);
4487 mii.cbSize = sizeof( mii);
4488 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4489 (LPMENUITEMINFOW)&mii, FALSE);
4490 mii.cbSize = lpmii->cbSize;
4491 memcpy( lpmii, &mii, mii.cbSize);
4495 /**********************************************************************
4496 * GetMenuItemInfoW (USER32.@)
4498 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4499 LPMENUITEMINFOW lpmii)
4503 if( lpmii->cbSize != sizeof( mii) &&
4504 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4505 SetLastError( ERROR_INVALID_PARAMETER);
4508 memcpy( &mii, lpmii, lpmii->cbSize);
4509 mii.cbSize = sizeof( mii);
4510 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4511 mii.cbSize = lpmii->cbSize;
4512 memcpy( lpmii, &mii, mii.cbSize);
4517 /* set a menu item text from a ASCII or Unicode string */
4518 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4524 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4525 strcpyW( menu->text, text );
4529 LPCSTR str = (LPCSTR)text;
4530 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4531 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4532 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4537 /**********************************************************************
4538 * SetMenuItemInfo_common
4541 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4542 const MENUITEMINFOW *lpmii,
4545 if (!menu) return FALSE;
4547 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4549 if (lpmii->fMask & MIIM_TYPE ) {
4550 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4551 WARN("invalid combination of fMask bits used\n");
4552 /* this does not happen on Win9x/ME */
4553 SetLastError( ERROR_INVALID_PARAMETER);
4557 /* Remove the old type bits and replace them with the new ones */
4558 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4559 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4561 if (IS_STRING_ITEM(menu->fType)) {
4562 HeapFree(GetProcessHeap(), 0, menu->text);
4563 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4564 } else if( (menu->fType) & MFT_BITMAP)
4565 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4568 if (lpmii->fMask & MIIM_FTYPE ) {
4569 if(( lpmii->fType & MFT_BITMAP)) {
4570 SetLastError( ERROR_INVALID_PARAMETER);
4573 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4574 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4576 if (lpmii->fMask & MIIM_STRING ) {
4577 /* free the string when used */
4578 HeapFree(GetProcessHeap(), 0, menu->text);
4579 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4582 if (lpmii->fMask & MIIM_STATE)
4584 /* Other menu items having MFS_DEFAULT are not converted
4586 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4589 if (lpmii->fMask & MIIM_ID)
4590 menu->wID = lpmii->wID;
4592 if (lpmii->fMask & MIIM_SUBMENU) {
4593 menu->hSubMenu = lpmii->hSubMenu;
4594 if (menu->hSubMenu) {
4595 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4597 subMenu->wFlags |= MF_POPUP;
4598 menu->fType |= MF_POPUP;
4601 SetLastError( ERROR_INVALID_PARAMETER);
4606 menu->fType &= ~MF_POPUP;
4609 if (lpmii->fMask & MIIM_CHECKMARKS)
4611 menu->hCheckBit = lpmii->hbmpChecked;
4612 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4614 if (lpmii->fMask & MIIM_DATA)
4615 menu->dwItemData = lpmii->dwItemData;
4617 if (lpmii->fMask & MIIM_BITMAP)
4618 menu->hbmpItem = lpmii->hbmpItem;
4620 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4621 menu->fType |= MFT_SEPARATOR;
4623 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4627 /**********************************************************************
4628 * SetMenuItemInfoA (USER32.@)
4630 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4631 const MENUITEMINFOA *lpmii)
4634 if( lpmii->cbSize != sizeof( mii) &&
4635 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4636 SetLastError( ERROR_INVALID_PARAMETER);
4639 memcpy( &mii, lpmii, lpmii->cbSize);
4640 if( lpmii->cbSize != sizeof( mii)) {
4641 mii.cbSize = sizeof( mii);
4642 mii.hbmpItem = NULL;
4644 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4645 (const MENUITEMINFOW *)&mii, FALSE);
4648 /**********************************************************************
4649 * SetMenuItemInfoW (USER32.@)
4651 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4652 const MENUITEMINFOW *lpmii)
4655 if( lpmii->cbSize != sizeof( mii) &&
4656 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4657 SetLastError( ERROR_INVALID_PARAMETER);
4660 memcpy( &mii, lpmii, lpmii->cbSize);
4661 if( lpmii->cbSize != sizeof( mii)) {
4662 mii.cbSize = sizeof( mii);
4663 mii.hbmpItem = NULL;
4665 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4666 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4669 /**********************************************************************
4670 * SetMenuDefaultItem (USER32.@)
4673 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4679 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4681 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4683 /* reset all default-item flags */
4685 for (i = 0; i < menu->nItems; i++, item++)
4687 item->fState &= ~MFS_DEFAULT;
4690 /* no default item */
4699 if ( uItem >= menu->nItems ) return FALSE;
4700 item[uItem].fState |= MFS_DEFAULT;
4705 for (i = 0; i < menu->nItems; i++, item++)
4707 if (item->wID == uItem)
4709 item->fState |= MFS_DEFAULT;
4718 /**********************************************************************
4719 * GetMenuDefaultItem (USER32.@)
4721 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4727 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4729 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4731 /* find default item */
4735 if (! item) return -1;
4737 while ( !( item->fState & MFS_DEFAULT ) )
4740 if (i >= menu->nItems ) return -1;
4743 /* default: don't return disabled items */
4744 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4746 /* search rekursiv when needed */
4747 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4750 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4751 if ( -1 != ret ) return ret;
4753 /* when item not found in submenu, return the popup item */
4755 return ( bypos ) ? i : item->wID;
4760 /**********************************************************************
4761 * InsertMenuItemA (USER32.@)
4763 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4764 const MENUITEMINFOA *lpmii)
4766 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4768 if( lpmii->cbSize != sizeof( mii) &&
4769 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4770 SetLastError( ERROR_INVALID_PARAMETER);
4773 memcpy( &mii, lpmii, lpmii->cbSize);
4774 if( lpmii->cbSize != sizeof( mii)) {
4775 mii.cbSize = sizeof( mii);
4776 mii.hbmpItem = NULL;
4778 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4782 /**********************************************************************
4783 * InsertMenuItemW (USER32.@)
4785 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4786 const MENUITEMINFOW *lpmii)
4788 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4790 if( lpmii->cbSize != sizeof( mii) &&
4791 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4792 SetLastError( ERROR_INVALID_PARAMETER);
4795 memcpy( &mii, lpmii, lpmii->cbSize);
4796 if( lpmii->cbSize != sizeof( mii)) {
4797 mii.cbSize = sizeof( mii);
4798 mii.hbmpItem = NULL;
4800 return SetMenuItemInfo_common(item, &mii, TRUE);
4803 /**********************************************************************
4804 * CheckMenuRadioItem (USER32.@)
4807 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4808 UINT first, UINT last, UINT check,
4811 MENUITEM *mifirst, *milast, *micheck;
4812 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4814 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4816 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4817 milast = MENU_FindItem (&mlast, &last, bypos);
4818 micheck = MENU_FindItem (&mcheck, &check, bypos);
4820 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4821 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4822 micheck > milast || micheck < mifirst)
4825 while (mifirst <= milast)
4827 if (mifirst == micheck)
4829 mifirst->fType |= MFT_RADIOCHECK;
4830 mifirst->fState |= MFS_CHECKED;
4832 mifirst->fType &= ~MFT_RADIOCHECK;
4833 mifirst->fState &= ~MFS_CHECKED;
4842 /**********************************************************************
4843 * GetMenuItemRect (USER32.@)
4845 * ATTENTION: Here, the returned values in rect are the screen
4846 * coordinates of the item just like if the menu was
4847 * always on the upper left side of the application.
4850 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4853 POPUPMENU *itemMenu;
4857 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4859 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4860 referenceHwnd = hwnd;
4864 itemMenu = MENU_GetMenu(hMenu);
4865 if (itemMenu == NULL)
4868 if(itemMenu->hWnd == 0)
4870 referenceHwnd = itemMenu->hWnd;
4873 if ((rect == NULL) || (item == NULL))
4878 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4884 /**********************************************************************
4885 * SetMenuInfo (USER32.@)
4888 * MIM_APPLYTOSUBMENUS
4889 * actually use the items to draw the menu
4891 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4895 TRACE("(%p %p)\n", hMenu, lpmi);
4897 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4900 if (lpmi->fMask & MIM_BACKGROUND)
4901 menu->hbrBack = lpmi->hbrBack;
4903 if (lpmi->fMask & MIM_HELPID)
4904 menu->dwContextHelpID = lpmi->dwContextHelpID;
4906 if (lpmi->fMask & MIM_MAXHEIGHT)
4907 menu->cyMax = lpmi->cyMax;
4909 if (lpmi->fMask & MIM_MENUDATA)
4910 menu->dwMenuData = lpmi->dwMenuData;
4912 if (lpmi->fMask & MIM_STYLE)
4914 menu->dwStyle = lpmi->dwStyle;
4915 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4916 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4917 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4918 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4926 /**********************************************************************
4927 * GetMenuInfo (USER32.@)
4933 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4936 TRACE("(%p %p)\n", hMenu, lpmi);
4938 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4941 if (lpmi->fMask & MIM_BACKGROUND)
4942 lpmi->hbrBack = menu->hbrBack;
4944 if (lpmi->fMask & MIM_HELPID)
4945 lpmi->dwContextHelpID = menu->dwContextHelpID;
4947 if (lpmi->fMask & MIM_MAXHEIGHT)
4948 lpmi->cyMax = menu->cyMax;
4950 if (lpmi->fMask & MIM_MENUDATA)
4951 lpmi->dwMenuData = menu->dwMenuData;
4953 if (lpmi->fMask & MIM_STYLE)
4954 lpmi->dwStyle = menu->dwStyle;
4962 /**********************************************************************
4963 * SetMenuContextHelpId (USER32.@)
4965 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4969 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
4971 if ((menu = MENU_GetMenu(hMenu)))
4973 menu->dwContextHelpID = dwContextHelpID;
4980 /**********************************************************************
4981 * GetMenuContextHelpId (USER32.@)
4983 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4987 TRACE("(%p)\n", hMenu);
4989 if ((menu = MENU_GetMenu(hMenu)))
4991 return menu->dwContextHelpID;
4996 /**********************************************************************
4997 * MenuItemFromPoint (USER32.@)
4999 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5001 POPUPMENU *menu = MENU_GetMenu(hMenu);
5004 /*FIXME: Do we have to handle hWnd here? */
5005 if (!menu) return -1;
5006 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5011 /**********************************************************************
5012 * translate_accelerator
5014 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5015 BYTE fVirt, WORD key, WORD cmd )
5020 if (wParam != key) return FALSE;
5022 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5023 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5024 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5026 if (message == WM_CHAR || message == WM_SYSCHAR)
5028 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5030 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5036 if(fVirt & FVIRTKEY)
5038 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5039 wParam, 0xff & HIWORD(lParam));
5041 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5042 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5046 if (!(lParam & 0x01000000)) /* no special_key */
5048 if ((fVirt & FALT) && (lParam & 0x20000000))
5049 { /* ^^ ALT pressed */
5050 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5059 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5063 HMENU hMenu, hSubMenu, hSysMenu;
5064 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5066 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5067 hSysMenu = get_win_sys_menu( hWnd );
5069 /* find menu item and ask application to initialize it */
5070 /* 1. in the system menu */
5071 hSubMenu = hSysMenu;
5073 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5077 if (!IsWindowEnabled(hWnd))
5081 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5082 if(hSubMenu != hSysMenu)
5084 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5085 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5086 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5088 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5091 else /* 2. in the window's menu */
5095 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5099 if (!IsWindowEnabled(hWnd))
5103 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5104 if(hSubMenu != hMenu)
5106 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5107 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5108 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5110 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5117 if (uSysStat != (UINT)-1)
5119 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5126 if (uStat != (UINT)-1)
5132 if (uStat & (MF_DISABLED|MF_GRAYED))
5144 if( mesg==WM_COMMAND )
5146 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5147 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5149 else if( mesg==WM_SYSCOMMAND )
5151 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5152 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5156 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5157 * #0: unknown (please report!)
5158 * #1: for WM_KEYUP,WM_SYSKEYUP
5159 * #2: mouse is captured
5160 * #3: window is disabled
5161 * #4: it's a disabled system menu option
5162 * #5: it's a menu option, but window is iconic
5163 * #6: it's a menu option, but disabled
5165 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5167 ERR_(accel)(" unknown reason - please report!\n");
5172 /**********************************************************************
5173 * TranslateAcceleratorA (USER32.@)
5174 * TranslateAccelerator (USER32.@)
5176 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5179 LPACCEL16 lpAccelTbl;
5183 if (!hWnd || !msg) return 0;
5185 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5187 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5191 wParam = msg->wParam;
5193 switch (msg->message)
5202 char ch = LOWORD(wParam);
5204 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5205 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5213 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5214 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5218 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5219 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5221 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5226 /**********************************************************************
5227 * TranslateAcceleratorW (USER32.@)
5229 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5232 LPACCEL16 lpAccelTbl;
5235 if (!hWnd || !msg) return 0;
5237 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5239 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5243 switch (msg->message)
5255 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5256 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5260 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5261 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5263 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);