4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Note: the style MF_MOUSESELECT is used to mark popup items that
26 * have been selected, i.e. their popup menu is currently displayed.
27 * This is probably not the meaning this style has in MS-Windows.
39 #include "wine/port.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* internal popup menu window messages */
63 #define MM_SETMENUHANDLE (WM_USER + 0)
64 #define MM_GETMENUHANDLE (WM_USER + 1)
66 /* Menu item structure */
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT_PTR wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
84 /* Popup menu structure */
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103 SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
106 /* internal flags for menu tracking */
108 #define TF_ENDMENU 0x0001
109 #define TF_SUSPENDPOPUP 0x0002
110 #define TF_SKIPREMOVE 0x0004
115 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116 HMENU hTopMenu; /* initial menu */
117 HWND hOwnerWnd; /* where notifications are sent */
121 #define MENU_MAGIC 0x554d /* 'MU' */
126 /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL 0xF0000000
128 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* popup menu shade thickness */
133 #define POPUP_XSHADE 4
134 #define POPUP_YSHADE 4
136 /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
139 /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
142 /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
152 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
153 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
159 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
160 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
161 MF_POPUP | MF_SYSMENU | MF_HELP)
162 #define STATE_MASK (~TYPE_MASK)
164 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
166 /* Dimension of the menu bitmaps */
167 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
169 static HBITMAP hStdMnArrow = 0;
170 static HBITMAP hBmpSysMenu = 0;
172 static HBRUSH hShadeBrush = 0;
173 static HFONT hMenuFont = 0;
174 static HFONT hMenuFontBold = 0;
176 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
182 /* Flag set by EndMenu() to force an exit from menu tracking */
183 static BOOL fEndMenu = FALSE;
185 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
187 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
189 /*********************************************************************
190 * menu class descriptor
192 const struct builtin_class_descr MENU_builtin_class =
194 POPUPMENU_CLASS_ATOMA, /* name */
195 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
196 NULL, /* procA (winproc is Unicode only) */
197 PopupMenuWndProc, /* procW */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
213 #define MENUOUT(text) \
214 DPRINTF("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
221 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
224 TRACE("%s ", prefix);
226 UINT flags = mp->fType;
227 int type = MENU_ITEM_TYPE(flags);
228 DPRINTF( "{ ID=0x%x", mp->wID);
229 if (flags & MF_POPUP)
230 DPRINTF( ", Sub=%p", mp->hSubMenu);
234 if (type == MFT_STRING)
236 else if (type == MFT_SEPARATOR)
238 else if (type == MFT_OWNERDRAW)
240 else if (type == MFT_BITMAP)
246 MENUFLAG(MF_POPUP, "pop");
247 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
248 MENUFLAG(MFT_MENUBREAK, "brk");
249 MENUFLAG(MFT_RADIOCHECK, "radio");
250 MENUFLAG(MFT_RIGHTORDER, "rorder");
251 MENUFLAG(MF_SYSMENU, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
255 DPRINTF( "+0x%x", flags);
260 DPRINTF( ", State=");
261 MENUFLAG(MFS_GRAYED, "grey");
262 MENUFLAG(MFS_DEFAULT, "default");
263 MENUFLAG(MFS_DISABLED, "dis");
264 MENUFLAG(MFS_CHECKED, "check");
265 MENUFLAG(MFS_HILITE, "hi");
266 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
267 MENUFLAG(MF_MOUSESELECT, "mouse");
269 DPRINTF( "+0x%x", flags);
272 DPRINTF( ", Chk=%p", mp->hCheckBit);
274 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
276 if (type == MFT_STRING) {
278 DPRINTF( ", Text=%s", debugstr_w(mp->text));
280 DPRINTF( ", Text=Null");
281 } else if (mp->text == NULL)
284 DPRINTF( ", Text=%p", mp->text);
286 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
292 DPRINTF(" %s\n", postfix);
299 /***********************************************************************
302 * Validate the given menu handle and returns the menu structure pointer.
304 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
306 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
307 if (!menu || menu->wMagic != MENU_MAGIC)
309 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
315 /***********************************************************************
318 * Get the system menu of a window
320 static HMENU get_win_sys_menu( HWND hwnd )
323 WND *win = WIN_GetPtr( hwnd );
324 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
327 WIN_ReleasePtr( win );
332 /***********************************************************************
335 * Return the default system menu.
337 static HMENU MENU_CopySysPopup(void)
339 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
340 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
343 POPUPMENU* menu = MENU_GetMenu(hMenu);
344 menu->wFlags |= MF_SYSMENU | MF_POPUP;
345 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
348 ERR("Unable to load default system menu\n" );
350 TRACE("returning %p.\n", hMenu );
356 /**********************************************************************
359 * Create a copy of the system menu. System menu in Windows is
360 * a special menu bar with the single entry - system menu popup.
361 * This popup is presented to the outside world as a "system menu".
362 * However, the real system menu handle is sometimes seen in the
363 * WM_MENUSELECT parameters (and Word 6 likes it this way).
365 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
369 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
370 if ((hMenu = CreateMenu()))
372 POPUPMENU *menu = MENU_GetMenu(hMenu);
373 menu->wFlags = MF_SYSMENU;
374 menu->hWnd = WIN_GetFullHandle( hWnd );
375 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
377 if (hPopupMenu == (HMENU)(-1))
378 hPopupMenu = MENU_CopySysPopup();
379 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
383 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
384 (UINT_PTR)hPopupMenu, NULL );
386 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
387 menu->items[0].fState = 0;
388 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
390 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
393 DestroyMenu( hMenu );
395 ERR("failed to load system menu!\n");
400 /***********************************************************************
403 * Menus initialisation.
408 NONCLIENTMETRICSW ncm;
410 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
415 /* Load menu bitmaps */
416 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
417 /* Load system buttons bitmaps */
418 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
423 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
424 arrow_bitmap_width = bm.bmWidth;
425 arrow_bitmap_height = bm.bmHeight;
429 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
432 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
435 DeleteObject( hBitmap );
436 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
439 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
440 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
443 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
446 ncm.lfMenuFont.lfWeight += 300;
447 if ( ncm.lfMenuFont.lfWeight > 1000)
448 ncm.lfMenuFont.lfWeight = 1000;
450 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
456 /***********************************************************************
457 * MENU_InitSysMenuPopup
459 * Grey the appropriate items in System menu.
461 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
465 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
466 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
467 gray = ((style & WS_MAXIMIZE) != 0);
468 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
469 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
470 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
471 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
472 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
473 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
474 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
475 gray = (clsStyle & CS_NOCLOSE) != 0;
477 /* The menu item must keep its state if it's disabled */
479 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
483 /******************************************************************************
485 * UINT MENU_GetStartOfNextColumn(
488 *****************************************************************************/
490 static UINT MENU_GetStartOfNextColumn(
493 POPUPMENU *menu = MENU_GetMenu(hMenu);
497 return NO_SELECTED_ITEM;
499 i = menu->FocusedItem + 1;
500 if( i == NO_SELECTED_ITEM )
503 for( ; i < menu->nItems; ++i ) {
504 if (menu->items[i].fType & MF_MENUBARBREAK)
508 return NO_SELECTED_ITEM;
512 /******************************************************************************
514 * UINT MENU_GetStartOfPrevColumn(
517 *****************************************************************************/
519 static UINT MENU_GetStartOfPrevColumn(
522 POPUPMENU *menu = MENU_GetMenu(hMenu);
526 return NO_SELECTED_ITEM;
528 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
529 return NO_SELECTED_ITEM;
531 /* Find the start of the column */
533 for(i = menu->FocusedItem; i != 0 &&
534 !(menu->items[i].fType & MF_MENUBARBREAK);
538 return NO_SELECTED_ITEM;
540 for(--i; i != 0; --i) {
541 if (menu->items[i].fType & MF_MENUBARBREAK)
545 TRACE("ret %d.\n", i );
552 /***********************************************************************
555 * Find a menu item. Return a pointer on the item, and modifies *hmenu
556 * in case the item was in a sub-menu.
558 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
563 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
564 if (wFlags & MF_BYPOSITION)
566 if (*nPos >= menu->nItems) return NULL;
567 return &menu->items[*nPos];
571 MENUITEM *item = menu->items;
572 for (i = 0; i < menu->nItems; i++, item++)
574 if (item->wID == *nPos)
579 else if (item->fType & MF_POPUP)
581 HMENU hsubmenu = item->hSubMenu;
582 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
594 /***********************************************************************
597 * Find a Sub menu. Return the position of the submenu, and modifies
598 * *hmenu in case it is found in another sub-menu.
599 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
601 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
606 if (((*hmenu)==(HMENU)0xffff) ||
607 (!(menu = MENU_GetMenu(*hmenu))))
608 return NO_SELECTED_ITEM;
610 for (i = 0; i < menu->nItems; i++, item++) {
611 if(!(item->fType & MF_POPUP)) continue;
612 if (item->hSubMenu == hSubTarget) {
616 HMENU hsubmenu = item->hSubMenu;
617 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
618 if (pos != NO_SELECTED_ITEM) {
624 return NO_SELECTED_ITEM;
627 /***********************************************************************
630 static void MENU_FreeItemData( MENUITEM* item )
633 if (IS_STRING_ITEM(item->fType) && item->text)
634 HeapFree( GetProcessHeap(), 0, item->text );
637 /***********************************************************************
638 * MENU_FindItemByCoords
640 * Find the item at the specified coordinates (screen coords). Does
641 * not work for child windows and therefore should not be called for
642 * an arbitrary system menu.
644 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
645 POINT pt, UINT *pos )
651 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
652 pt.x -= wrect.left;pt.y -= wrect.top;
654 for (i = 0; i < menu->nItems; i++, item++)
656 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
657 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
667 /***********************************************************************
670 * Find the menu item selected by a key press.
671 * Return item id, -1 if none, -2 if we should close the menu.
673 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
674 WCHAR key, BOOL forceMenuChar )
676 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
678 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
682 POPUPMENU *menu = MENU_GetMenu( hmenu );
683 MENUITEM *item = menu->items;
690 for (i = 0; i < menu->nItems; i++, item++)
692 if (IS_STRING_ITEM(item->fType) && item->text)
694 WCHAR *p = item->text - 2;
697 p = strchrW (p + 2, '&');
699 while (p != NULL && p [1] == '&');
700 if (p && (toupperW(p[1]) == toupperW(key))) return i;
704 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
705 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
706 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
707 if (HIWORD(menuchar) == 1) return (UINT)(-2);
713 /***********************************************************************
714 * MENU_GetBitmapItemSize
716 * Get the size of a bitmap item.
718 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
721 HBITMAP bmp = (HBITMAP)id;
723 size->cx = size->cy = 0;
725 /* check if there is a magic menu item associated with this item */
726 if (id && IS_MAGIC_ITEM( id ))
730 case (INT_PTR)HBMMENU_SYSTEM:
737 case (INT_PTR)HBMMENU_MBAR_RESTORE:
738 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
739 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
740 case (INT_PTR)HBMMENU_MBAR_CLOSE:
741 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
742 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
745 case (INT_PTR)HBMMENU_CALLBACK:
746 case (INT_PTR)HBMMENU_POPUP_CLOSE:
747 case (INT_PTR)HBMMENU_POPUP_RESTORE:
748 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
749 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
751 FIXME("Magic 0x%08x not implemented\n", id);
755 if (GetObjectW(bmp, sizeof(bm), &bm ))
757 size->cx = bm.bmWidth;
758 size->cy = bm.bmHeight;
762 /***********************************************************************
763 * MENU_DrawBitmapItem
765 * Draw a bitmap item.
766 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
768 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
773 HBITMAP bmp = (HBITMAP)lpitem->text;
774 int w = rect->right - rect->left;
775 int h = rect->bottom - rect->top;
778 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
780 /* Check if there is a magic menu item associated with this item */
781 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
786 switch(LOWORD(hbmToDraw))
788 case (INT_PTR)HBMMENU_SYSTEM:
789 if (lpitem->dwItemData)
791 bmp = (HBITMAP)lpitem->dwItemData;
792 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
797 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
798 /* only use right half of the bitmap */
799 bmp_xoffset = bm.bmWidth / 2;
800 bm.bmWidth -= bmp_xoffset;
803 case (INT_PTR)HBMMENU_MBAR_RESTORE:
804 flags = DFCS_CAPTIONRESTORE;
806 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
807 flags = DFCS_CAPTIONMIN;
809 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
810 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 flags = DFCS_CAPTIONCLOSE;
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
818 case (INT_PTR)HBMMENU_CALLBACK:
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
824 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
828 InflateRect( &r, -1, -1 );
829 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
830 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
834 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
837 hdcMem = CreateCompatibleDC( hdc );
838 SelectObject( hdcMem, bmp );
840 /* handle fontsize > bitmap_height */
841 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
843 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
844 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
845 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
846 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
851 /***********************************************************************
854 * Calculate the size of the menu item and store it in lpitem->rect.
856 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
857 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
860 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
862 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
863 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
864 (menuBar ? " (MenuBar)" : ""));
866 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
868 if (lpitem->fType & MF_OWNERDRAW)
871 ** Experimentation under Windows reveals that an owner-drawn
872 ** menu is expected to return the size of the content part of
873 ** the menu item, not including the checkmark nor the submenu
874 ** arrow. Windows adds those values itself and returns the
875 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
877 MEASUREITEMSTRUCT mis;
878 mis.CtlType = ODT_MENU;
880 mis.itemID = lpitem->wID;
881 mis.itemData = (DWORD)lpitem->dwItemData;
884 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
885 lpitem->rect.right += mis.itemWidth;
889 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
892 /* under at least win95 you seem to be given a standard
893 height for the menu and the height value is ignored */
894 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
897 lpitem->rect.bottom += mis.itemHeight;
899 TRACE("id=%04x size=%dx%d\n",
900 lpitem->wID, mis.itemWidth, mis.itemHeight);
901 /* Fall through to get check/arrow width calculation. */
904 if (lpitem->fType & MF_SEPARATOR)
906 lpitem->rect.bottom += SEPARATOR_HEIGHT;
912 /* New style MIIM_BITMAP */
913 if (lpitem->hbmpItem)
915 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
917 MEASUREITEMSTRUCT measItem;
918 measItem.CtlType = ODT_MENU;
920 measItem.itemID = lpitem->wID;
921 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
922 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
923 measItem.itemData = lpitem->dwItemData;
925 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
927 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
928 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
929 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
930 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
933 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
934 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
935 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
936 lpitem->rect.right += size.cx;
937 lpitem->rect.bottom += size.cy;
939 if (lppop->dwStyle & MNS_CHECKORBMP)
940 lpitem->rect.right += check_bitmap_width;
942 lpitem->rect.right += 2 * check_bitmap_width;
944 lpitem->rect.right += 2 * check_bitmap_width;
945 if (lpitem->fType & MF_POPUP)
946 lpitem->rect.right += arrow_bitmap_width;
949 if (lpitem->fType & MF_OWNERDRAW)
952 if (IS_BITMAP_ITEM(lpitem->fType))
956 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
957 lpitem->rect.right += size.cx;
958 lpitem->rect.bottom += size.cy;
959 /* Leave space for the sunken border */
960 lpitem->rect.right += 2;
961 lpitem->rect.bottom += 2;
965 /* it must be a text item - unless it's the system menu */
966 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
969 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
971 lpitem->rect.right += size.cx;
972 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
977 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
979 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
981 /* Item contains a tab (only meaningful in popup menus) */
982 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
983 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
984 lpitem->rect.right += MENU_TAB_SPACE;
988 if (strchrW( lpitem->text, '\b' ))
989 lpitem->rect.right += MENU_TAB_SPACE;
990 lpitem->xTab = lpitem->rect.right - check_bitmap_width
991 - arrow_bitmap_width;
994 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
998 /***********************************************************************
999 * MENU_PopupMenuCalcSize
1001 * Calculate the size of a popup menu.
1003 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1008 int orgX, orgY, maxX, maxTab, maxTabWidth;
1010 lppop->Width = lppop->Height = 0;
1011 if (lppop->nItems == 0) return;
1014 SelectObject( hdc, hMenuFont);
1019 lppop->maxBmpSize.cx = 0;
1020 lppop->maxBmpSize.cy = 0;
1022 while (start < lppop->nItems)
1024 lpitem = &lppop->items[start];
1028 maxTab = maxTabWidth = 0;
1029 /* Parse items until column break or end of menu */
1030 for (i = start; i < lppop->nItems; i++, lpitem++)
1033 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1035 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1037 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1038 maxX = max( maxX, lpitem->rect.right );
1039 orgY = lpitem->rect.bottom;
1040 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1042 maxTab = max( maxTab, lpitem->xTab );
1043 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1047 /* Finish the column (set all items to the largest width found) */
1048 maxX = max( maxX, maxTab + maxTabWidth );
1049 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1051 lpitem->rect.right = maxX;
1052 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1053 lpitem->xTab = maxTab;
1056 lppop->Height = max( lppop->Height, orgY );
1059 lppop->Width = maxX;
1061 /* space for 3d border */
1065 ReleaseDC( 0, hdc );
1069 /***********************************************************************
1070 * MENU_MenuBarCalcSize
1072 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1073 * height is off by 1 pixel which causes lengthy window relocations when
1074 * active document window is maximized/restored.
1076 * Calculate the size of the menu bar.
1078 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1079 LPPOPUPMENU lppop, HWND hwndOwner )
1082 int start, i, orgX, orgY, maxY, helpPos;
1084 if ((lprect == NULL) || (lppop == NULL)) return;
1085 if (lppop->nItems == 0) return;
1086 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1087 lprect->left, lprect->top, lprect->right, lprect->bottom);
1088 lppop->Width = lprect->right - lprect->left;
1090 maxY = lprect->top+1;
1093 lppop->maxBmpSize.cx = 0;
1094 lppop->maxBmpSize.cy = 0;
1095 while (start < lppop->nItems)
1097 lpitem = &lppop->items[start];
1098 orgX = lprect->left;
1101 /* Parse items until line break or end of menu */
1102 for (i = start; i < lppop->nItems; i++, lpitem++)
1104 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1106 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1108 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1110 debug_print_menuitem (" item: ", lpitem, "");
1111 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1113 if (lpitem->rect.right > lprect->right)
1115 if (i != start) break;
1116 else lpitem->rect.right = lprect->right;
1118 maxY = max( maxY, lpitem->rect.bottom );
1119 orgX = lpitem->rect.right;
1122 /* Finish the line (set all items to the largest height found) */
1123 while (start < i) lppop->items[start++].rect.bottom = maxY;
1126 lprect->bottom = maxY;
1127 lppop->Height = lprect->bottom - lprect->top;
1129 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1130 /* the last item (if several lines, only move the last line) */
1131 lpitem = &lppop->items[lppop->nItems-1];
1132 orgY = lpitem->rect.top;
1133 orgX = lprect->right;
1134 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1135 if ( (helpPos==-1) || (helpPos>i) )
1137 if (lpitem->rect.top != orgY) break; /* Other line */
1138 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1139 lpitem->rect.left += orgX - lpitem->rect.right;
1140 lpitem->rect.right = orgX;
1141 orgX = lpitem->rect.left;
1145 /***********************************************************************
1148 * Draw a single menu item.
1150 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1151 UINT height, BOOL menuBar, UINT odaction )
1155 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1157 if (lpitem->fType & MF_SYSMENU)
1159 if( !IsIconic(hwnd) )
1160 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1166 if (lpitem->fState & MF_HILITE)
1169 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1170 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1172 if(lpitem->fState & MF_GRAYED)
1173 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1175 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1176 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1181 if (lpitem->fState & MF_GRAYED)
1182 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1184 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1185 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1188 if (lpitem->fType & MF_OWNERDRAW)
1191 ** Experimentation under Windows reveals that an owner-drawn
1192 ** menu is given the rectangle which includes the space it requested
1193 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1194 ** and a popup-menu arrow. This is the value of lpitem->rect.
1195 ** Windows will leave all drawing to the application except for
1196 ** the popup-menu arrow. Windows always draws that itself, after
1197 ** the menu owner has finished drawing.
1201 dis.CtlType = ODT_MENU;
1203 dis.itemID = lpitem->wID;
1204 dis.itemData = (DWORD)lpitem->dwItemData;
1206 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1207 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1208 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1209 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1210 dis.hwndItem = (HWND)hmenu;
1212 dis.rcItem = lpitem->rect;
1213 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1214 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1215 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1216 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1218 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1219 /* Fall through to draw popup-menu arrow */
1222 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1223 lpitem->rect.right,lpitem->rect.bottom);
1225 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1227 rect = lpitem->rect;
1229 if (!(lpitem->fType & MF_OWNERDRAW))
1231 if (lpitem->fState & MF_HILITE)
1234 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1236 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1239 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1242 SetBkMode( hdc, TRANSPARENT );
1244 if (!(lpitem->fType & MF_OWNERDRAW))
1246 /* vertical separator */
1247 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1251 rc.bottom = height - 3;
1252 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1255 /* horizontal separator */
1256 if (lpitem->fType & MF_SEPARATOR)
1261 rc.top += SEPARATOR_HEIGHT / 2;
1262 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1267 /* helper lines for debugging */
1268 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1269 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1270 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1271 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1277 INT y = rect.top + rect.bottom;
1278 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1279 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1281 if (!(lpitem->fType & MF_OWNERDRAW))
1283 /* New style MIIM_BITMAP */
1284 if (lpitem->hbmpItem)
1286 POPUPMENU *menu = MENU_GetMenu(hmenu);
1287 HBITMAP hbm = lpitem->hbmpItem;
1289 if (hbm == HBMMENU_CALLBACK)
1291 DRAWITEMSTRUCT drawItem;
1292 drawItem.CtlType = ODT_MENU;
1294 drawItem.itemID = lpitem->wID;
1295 drawItem.itemAction = odaction;
1296 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1297 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1298 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1299 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1300 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1301 drawItem.hwndItem = (HWND)hmenu;
1303 drawItem.rcItem = lpitem->rect;
1304 drawItem.itemData = lpitem->dwItemData;
1306 if (!(lpitem->fState & MF_CHECKED))
1307 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1310 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1312 if (menu->dwStyle & MNS_CHECKORBMP)
1313 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1315 rect.left += menu->maxBmpSize.cx;
1317 /* Draw the check mark
1320 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1322 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1323 if (bm) /* we have a custom bitmap */
1325 HDC hdcMem = CreateCompatibleDC( hdc );
1326 SelectObject( hdcMem, bm );
1327 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1328 check_bitmap_width, check_bitmap_height,
1329 hdcMem, 0, 0, SRCCOPY );
1332 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1335 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1336 HDC hdcMem = CreateCompatibleDC( hdc );
1337 SelectObject( hdcMem, bm );
1338 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1339 DrawFrameControl( hdcMem, &r, DFC_MENU,
1340 (lpitem->fType & MFT_RADIOCHECK) ?
1341 DFCS_MENUBULLET : DFCS_MENUCHECK );
1342 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1343 hdcMem, 0, 0, SRCCOPY );
1349 /* Draw the popup-menu arrow */
1350 if (lpitem->fType & MF_POPUP)
1352 HDC hdcMem = CreateCompatibleDC( hdc );
1353 HBITMAP hOrigBitmap;
1355 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1356 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1357 (y - arrow_bitmap_height) / 2,
1358 arrow_bitmap_width, arrow_bitmap_height,
1359 hdcMem, 0, 0, SRCCOPY );
1360 SelectObject( hdcMem, hOrigBitmap );
1364 rect.left += check_bitmap_width;
1365 rect.right -= arrow_bitmap_width;
1368 /* Done for owner-drawn */
1369 if (lpitem->fType & MF_OWNERDRAW)
1372 /* Draw the item text or bitmap */
1373 if (IS_BITMAP_ITEM(lpitem->fType))
1375 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1378 /* No bitmap - process text if present */
1379 else if (IS_STRING_ITEM(lpitem->fType))
1384 UINT uFormat = (menuBar) ?
1385 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1386 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1388 if ( lpitem->fState & MFS_DEFAULT )
1390 hfontOld = SelectObject( hdc, hMenuFontBold);
1395 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1396 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1399 for (i = 0; lpitem->text[i]; i++)
1400 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1403 if(lpitem->fState & MF_GRAYED)
1405 if (!(lpitem->fState & MF_HILITE) )
1407 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1408 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1409 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1410 --rect.left; --rect.top; --rect.right; --rect.bottom;
1412 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1415 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1417 /* paint the shortcut text */
1418 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1420 if (lpitem->text[i] == '\t')
1422 rect.left = lpitem->xTab;
1423 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1427 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1430 if(lpitem->fState & MF_GRAYED)
1432 if (!(lpitem->fState & MF_HILITE) )
1434 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1435 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1436 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1437 --rect.left; --rect.top; --rect.right; --rect.bottom;
1439 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1441 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1445 SelectObject (hdc, hfontOld);
1450 /***********************************************************************
1451 * MENU_DrawPopupMenu
1453 * Paint a popup menu.
1455 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1457 HBRUSH hPrevBrush = 0;
1460 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1462 GetClientRect( hwnd, &rect );
1464 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1465 && (SelectObject( hdc, hMenuFont)))
1469 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1471 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1476 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1478 /* draw menu items */
1480 menu = MENU_GetMenu( hmenu );
1481 if (menu && menu->nItems)
1486 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1487 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1488 menu->Height, FALSE, ODA_DRAWENTIRE );
1493 SelectObject( hdc, hPrevBrush );
1498 /***********************************************************************
1501 * Paint a menu bar. Returns the height of the menu bar.
1502 * called from [windows/nonclient.c]
1504 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1509 HMENU hMenu = GetMenu(hwnd);
1511 lppop = MENU_GetMenu( hMenu );
1512 if (lppop == NULL || lprect == NULL)
1514 return GetSystemMetrics(SM_CYMENU);
1519 hfontOld = SelectObject( hDC, hMenuFont);
1521 if (lppop->Height == 0)
1522 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1524 lprect->bottom = lprect->top + lppop->Height;
1526 if (hfontOld) SelectObject( hDC, hfontOld);
1527 return lppop->Height;
1530 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1534 /***********************************************************************
1537 * Display a popup menu.
1539 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1540 INT x, INT y, INT xanchor, INT yanchor )
1545 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1546 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1548 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1549 if (menu->FocusedItem != NO_SELECTED_ITEM)
1551 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1552 menu->FocusedItem = NO_SELECTED_ITEM;
1555 /* store the owner for DrawItem */
1556 menu->hwndOwner = hwndOwner;
1558 MENU_PopupMenuCalcSize( menu, hwndOwner );
1560 /* adjust popup menu pos so that it fits within the desktop */
1562 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1563 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1565 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1568 x -= width - xanchor;
1569 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1570 x = GetSystemMetrics(SM_CXSCREEN) - width;
1574 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1577 y -= height + yanchor;
1578 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1579 y = GetSystemMetrics(SM_CYSCREEN) - height;
1583 /* NOTE: In Windows, top menu popup is not owned. */
1584 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1585 WS_POPUP, x, y, width, height,
1586 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1588 if( !menu->hWnd ) return FALSE;
1589 if (!top_popup) top_popup = menu->hWnd;
1591 /* Display the window */
1593 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1594 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1595 UpdateWindow( menu->hWnd );
1600 /***********************************************************************
1603 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1604 BOOL sendMenuSelect, HMENU topmenu )
1609 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1611 lppop = MENU_GetMenu( hmenu );
1612 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1614 if (lppop->FocusedItem == wIndex) return;
1615 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1616 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1617 if (!top_popup) top_popup = lppop->hWnd;
1619 SelectObject( hdc, hMenuFont);
1621 /* Clear previous highlighted item */
1622 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1624 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1625 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1626 lppop->Height, !(lppop->wFlags & MF_POPUP),
1630 /* Highlight new item (if any) */
1631 lppop->FocusedItem = wIndex;
1632 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1634 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1635 lppop->items[wIndex].fState |= MF_HILITE;
1636 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1637 &lppop->items[wIndex], lppop->Height,
1638 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1642 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1643 SendMessageW( hwndOwner, WM_MENUSELECT,
1644 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1645 ip->fType | ip->fState |
1646 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1649 else if (sendMenuSelect) {
1652 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1653 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1654 MENUITEM *ip = &ptm->items[pos];
1655 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1656 ip->fType | ip->fState |
1657 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1661 ReleaseDC( lppop->hWnd, hdc );
1665 /***********************************************************************
1666 * MENU_MoveSelection
1668 * Moves currently selected item according to the offset parameter.
1669 * If there is no selection then it should select the last item if
1670 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1672 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1677 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1679 menu = MENU_GetMenu( hmenu );
1680 if ((!menu) || (!menu->items)) return;
1682 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1684 if( menu->nItems == 1 ) return; else
1685 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1687 if (!(menu->items[i].fType & MF_SEPARATOR))
1689 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1694 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1695 i >= 0 && i < menu->nItems ; i += offset)
1696 if (!(menu->items[i].fType & MF_SEPARATOR))
1698 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1704 /**********************************************************************
1707 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1710 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1713 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1715 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1716 TRACE("flags=%x str=%p\n", flags, str);
1718 if (IS_STRING_ITEM(flags))
1722 flags |= MF_SEPARATOR;
1728 /* Item beginning with a backspace is a help item */
1734 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1736 strcpyW( text, str );
1740 else if (IS_BITMAP_ITEM(flags))
1741 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1742 else item->text = NULL;
1744 if (flags & MF_OWNERDRAW)
1745 item->dwItemData = (DWORD)str;
1747 item->dwItemData = 0;
1749 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1750 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1752 if (flags & MF_POPUP)
1754 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1755 if (menu) menu->wFlags |= MF_POPUP;
1767 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1769 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1770 flags |= MF_POPUP; /* keep popup */
1772 item->fType = flags & TYPE_MASK;
1773 item->fState = (flags & STATE_MASK) &
1774 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1777 /* Don't call SetRectEmpty here! */
1780 HeapFree( GetProcessHeap(), 0, prevText );
1782 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1787 /**********************************************************************
1790 * Insert (allocate) a new item into a menu.
1792 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1797 if (!(menu = MENU_GetMenu(hMenu)))
1800 /* Find where to insert new item */
1802 if (flags & MF_BYPOSITION) {
1803 if (pos > menu->nItems)
1806 if (!MENU_FindItem( &hMenu, &pos, flags ))
1809 if (!(menu = MENU_GetMenu( hMenu )))
1814 /* Create new items array */
1816 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1819 WARN("allocation failed\n" );
1822 if (menu->nItems > 0)
1824 /* Copy the old array into the new one */
1825 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1826 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1827 (menu->nItems-pos)*sizeof(MENUITEM) );
1828 HeapFree( GetProcessHeap(), 0, menu->items );
1830 menu->items = newItems;
1832 memset( &newItems[pos], 0, sizeof(*newItems) );
1833 menu->Height = 0; /* force size recalculate */
1834 return &newItems[pos];
1838 /**********************************************************************
1839 * MENU_ParseResource
1841 * Parse a standard menu resource and add items to the menu.
1842 * Return a pointer to the end of the resource.
1844 * NOTE: flags is equivalent to the mtOption field
1846 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1853 flags = GET_WORD(res);
1854 res += sizeof(WORD);
1855 if (!(flags & MF_POPUP))
1858 res += sizeof(WORD);
1861 if (!unicode) res += strlen(str) + 1;
1862 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1863 if (flags & MF_POPUP)
1865 HMENU hSubMenu = CreatePopupMenu();
1866 if (!hSubMenu) return NULL;
1867 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1869 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1870 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1872 else /* Not a popup */
1874 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1875 else AppendMenuW( hMenu, flags, id,
1876 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1878 } while (!(flags & MF_END));
1883 /**********************************************************************
1884 * MENUEX_ParseResource
1886 * Parse an extended menu resource and add items to the menu.
1887 * Return a pointer to the end of the resource.
1889 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1895 mii.cbSize = sizeof(mii);
1896 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1897 mii.fType = GET_DWORD(res);
1898 res += sizeof(DWORD);
1899 mii.fState = GET_DWORD(res);
1900 res += sizeof(DWORD);
1901 mii.wID = GET_DWORD(res);
1902 res += sizeof(DWORD);
1903 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1904 res += sizeof(WORD);
1905 /* Align the text on a word boundary. */
1906 res += (~((int)res - 1)) & 1;
1907 mii.dwTypeData = (LPWSTR) res;
1908 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1909 /* Align the following fields on a dword boundary. */
1910 res += (~((int)res - 1)) & 3;
1912 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1913 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1915 if (resinfo & 1) { /* Pop-up? */
1916 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1917 res += sizeof(DWORD);
1918 mii.hSubMenu = CreatePopupMenu();
1921 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1922 DestroyMenu(mii.hSubMenu);
1925 mii.fMask |= MIIM_SUBMENU;
1926 mii.fType |= MF_POPUP;
1928 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1930 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1931 mii.wID, mii.fType);
1932 mii.fType |= MF_SEPARATOR;
1934 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1935 } while (!(resinfo & MF_END));
1940 /***********************************************************************
1943 * Return the handle of the selected sub-popup menu (if any).
1945 static HMENU MENU_GetSubPopup( HMENU hmenu )
1950 menu = MENU_GetMenu( hmenu );
1952 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1954 item = &menu->items[menu->FocusedItem];
1955 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1956 return item->hSubMenu;
1961 /***********************************************************************
1962 * MENU_HideSubPopups
1964 * Hide the sub-popup menus of this menu.
1966 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1967 BOOL sendMenuSelect )
1969 POPUPMENU *menu = MENU_GetMenu( hmenu );
1971 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1973 if (menu && top_popup)
1979 if (menu->FocusedItem != NO_SELECTED_ITEM)
1981 item = &menu->items[menu->FocusedItem];
1982 if (!(item->fType & MF_POPUP) ||
1983 !(item->fState & MF_MOUSESELECT)) return;
1984 item->fState &= ~MF_MOUSESELECT;
1985 hsubmenu = item->hSubMenu;
1988 submenu = MENU_GetMenu( hsubmenu );
1989 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1990 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1991 DestroyWindow( submenu->hWnd );
1997 /***********************************************************************
2000 * Display the sub-menu of the selected item of this menu.
2001 * Return the handle of the submenu, or hmenu if no submenu to display.
2003 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2004 BOOL selectFirst, UINT wFlags )
2011 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2013 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2015 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2017 item = &menu->items[menu->FocusedItem];
2018 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2021 /* message must be sent before using item,
2022 because nearly everything may be changed by the application ! */
2024 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2025 if (!(wFlags & TPM_NONOTIFY))
2026 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2027 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2029 item = &menu->items[menu->FocusedItem];
2032 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2033 if (!(item->fState & MF_HILITE))
2035 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2036 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2038 SelectObject( hdc, hMenuFont);
2040 item->fState |= MF_HILITE;
2041 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2042 ReleaseDC( menu->hWnd, hdc );
2044 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2047 item->fState |= MF_MOUSESELECT;
2049 if (IS_SYSTEM_MENU(menu))
2051 MENU_InitSysMenuPopup(item->hSubMenu,
2052 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2053 GetClassLongW( menu->hWnd, GCL_STYLE));
2055 NC_GetSysPopupPos( menu->hWnd, &rect );
2056 rect.top = rect.bottom;
2057 rect.right = GetSystemMetrics(SM_CXSIZE);
2058 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2062 GetWindowRect( menu->hWnd, &rect );
2063 if (menu->wFlags & MF_POPUP)
2065 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2066 rect.top += item->rect.top;
2067 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2068 rect.bottom = item->rect.top - item->rect.bottom;
2072 rect.left += item->rect.left;
2073 rect.top += item->rect.bottom;
2074 rect.right = item->rect.right - item->rect.left;
2075 rect.bottom = item->rect.bottom - item->rect.top;
2079 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2080 rect.left, rect.top, rect.right, rect.bottom );
2082 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2083 return item->hSubMenu;
2088 /**********************************************************************
2091 HWND MENU_IsMenuActive(void)
2096 /***********************************************************************
2099 * Walks menu chain trying to find a menu pt maps to.
2101 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2103 POPUPMENU *menu = MENU_GetMenu( hMenu );
2104 UINT item = menu->FocusedItem;
2107 /* try subpopup first (if any) */
2108 ret = (item != NO_SELECTED_ITEM &&
2109 (menu->items[item].fType & MF_POPUP) &&
2110 (menu->items[item].fState & MF_MOUSESELECT))
2111 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2113 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2115 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2116 if( menu->wFlags & MF_POPUP )
2118 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2120 else if (ht == HTSYSMENU)
2121 ret = get_win_sys_menu( menu->hWnd );
2122 else if (ht == HTMENU)
2123 ret = GetMenu( menu->hWnd );
2128 /***********************************************************************
2129 * MENU_ExecFocusedItem
2131 * Execute a menu item (for instance when user pressed Enter).
2132 * Return the wID of the executed item. Otherwise, -1 indicating
2133 * that no menu item was executed;
2134 * Have to receive the flags for the TrackPopupMenu options to avoid
2135 * sending unwanted message.
2138 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2141 POPUPMENU *menu = MENU_GetMenu( hMenu );
2143 TRACE("%p hmenu=%p\n", pmt, hMenu);
2145 if (!menu || !menu->nItems ||
2146 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2148 item = &menu->items[menu->FocusedItem];
2150 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2152 if (!(item->fType & MF_POPUP))
2154 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2156 /* If TPM_RETURNCMD is set you return the id, but
2157 do not send a message to the owner */
2158 if(!(wFlags & TPM_RETURNCMD))
2160 if( menu->wFlags & MF_SYSMENU )
2161 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2162 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2164 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2170 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2175 /***********************************************************************
2176 * MENU_SwitchTracking
2178 * Helper function for menu navigation routines.
2180 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2182 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2183 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2185 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2187 if( pmt->hTopMenu != hPtMenu &&
2188 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2190 /* both are top level menus (system and menu-bar) */
2191 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2192 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2193 pmt->hTopMenu = hPtMenu;
2195 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2196 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2200 /***********************************************************************
2203 * Return TRUE if we can go on with menu tracking.
2205 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2207 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2212 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2215 if( IS_SYSTEM_MENU(ptmenu) )
2216 item = ptmenu->items;
2218 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2222 if( ptmenu->FocusedItem != id )
2223 MENU_SwitchTracking( pmt, hPtMenu, id );
2225 /* If the popup menu is not already "popped" */
2226 if(!(item->fState & MF_MOUSESELECT ))
2228 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2233 /* Else the click was on the menu bar, finish the tracking */
2238 /***********************************************************************
2241 * Return the value of MENU_ExecFocusedItem if
2242 * the selected item was not a popup. Else open the popup.
2243 * A -1 return value indicates that we go on with menu tracking.
2246 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2248 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2253 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2256 if( IS_SYSTEM_MENU(ptmenu) )
2257 item = ptmenu->items;
2259 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2261 if( item && (ptmenu->FocusedItem == id ))
2263 if( !(item->fType & MF_POPUP) )
2264 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2266 /* If we are dealing with the top-level menu */
2267 /* and this is a click on an already "popped" item: */
2268 /* Stop the menu tracking and close the opened submenus */
2269 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2272 ptmenu->bTimeToHide = TRUE;
2278 /***********************************************************************
2281 * Return TRUE if we can go on with menu tracking.
2283 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2285 UINT id = NO_SELECTED_ITEM;
2286 POPUPMENU *ptmenu = NULL;
2290 ptmenu = MENU_GetMenu( hPtMenu );
2291 if( IS_SYSTEM_MENU(ptmenu) )
2294 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2297 if( id == NO_SELECTED_ITEM )
2299 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2300 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2303 else if( ptmenu->FocusedItem != id )
2305 MENU_SwitchTracking( pmt, hPtMenu, id );
2306 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2312 /***********************************************************************
2315 static void MENU_SetCapture( HWND hwnd )
2319 SERVER_START_REQ( set_capture_window )
2322 req->flags = CAPTURE_MENU;
2323 if (!wine_server_call_err( req ))
2325 previous = reply->previous;
2326 hwnd = reply->full_handle;
2331 if (previous && previous != hwnd)
2332 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2336 /***********************************************************************
2339 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2341 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2343 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2345 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2346 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2348 MDINEXTMENU next_menu;
2353 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2354 next_menu.hmenuNext = 0;
2355 next_menu.hwndNext = 0;
2356 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2358 TRACE("%p [%p] -> %p [%p]\n",
2359 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2361 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2363 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2364 hNewWnd = pmt->hOwnerWnd;
2365 if( IS_SYSTEM_MENU(menu) )
2367 /* switch to the menu bar */
2369 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2373 menu = MENU_GetMenu( hNewMenu );
2374 id = menu->nItems - 1;
2377 else if (style & WS_SYSMENU )
2379 /* switch to the system menu */
2380 hNewMenu = get_win_sys_menu( hNewWnd );
2384 else /* application returned a new menu to switch to */
2386 hNewMenu = next_menu.hmenuNext;
2387 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2389 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2391 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2393 if (style & WS_SYSMENU &&
2394 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2396 /* get the real system menu */
2397 hNewMenu = get_win_sys_menu(hNewWnd);
2399 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2401 /* FIXME: Not sure what to do here;
2402 * perhaps try to track hNewMenu as a popup? */
2404 TRACE(" -- got confused.\n");
2411 if( hNewMenu != pmt->hTopMenu )
2413 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2415 if( pmt->hCurrentMenu != pmt->hTopMenu )
2416 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2419 if( hNewWnd != pmt->hOwnerWnd )
2421 pmt->hOwnerWnd = hNewWnd;
2422 MENU_SetCapture( pmt->hOwnerWnd );
2425 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2426 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2433 /***********************************************************************
2436 * The idea is not to show the popup if the next input message is
2437 * going to hide it anyway.
2439 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2443 msg.hwnd = pmt->hOwnerWnd;
2445 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2446 pmt->trackFlags |= TF_SKIPREMOVE;
2451 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2452 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2454 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2455 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2456 if( msg.message == WM_KEYDOWN &&
2457 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2459 pmt->trackFlags |= TF_SUSPENDPOPUP;
2466 /* failures go through this */
2467 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2471 /***********************************************************************
2474 * Handle a VK_ESCAPE key event in a menu.
2476 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2478 BOOL bEndMenu = TRUE;
2480 if (pmt->hCurrentMenu != pmt->hTopMenu)
2482 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2484 if (menu->wFlags & MF_POPUP)
2486 HMENU hmenutmp, hmenuprev;
2488 hmenuprev = hmenutmp = pmt->hTopMenu;
2490 /* close topmost popup */
2491 while (hmenutmp != pmt->hCurrentMenu)
2493 hmenuprev = hmenutmp;
2494 hmenutmp = MENU_GetSubPopup( hmenuprev );
2497 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2498 pmt->hCurrentMenu = hmenuprev;
2506 /***********************************************************************
2509 * Handle a VK_LEFT key event in a menu.
2511 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2514 HMENU hmenutmp, hmenuprev;
2517 hmenuprev = hmenutmp = pmt->hTopMenu;
2518 menu = MENU_GetMenu( hmenutmp );
2520 /* Try to move 1 column left (if possible) */
2521 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2522 NO_SELECTED_ITEM ) {
2524 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2529 /* close topmost popup */
2530 while (hmenutmp != pmt->hCurrentMenu)
2532 hmenuprev = hmenutmp;
2533 hmenutmp = MENU_GetSubPopup( hmenuprev );
2536 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2537 pmt->hCurrentMenu = hmenuprev;
2539 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2541 /* move menu bar selection if no more popups are left */
2543 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2544 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2546 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2548 /* A sublevel menu was displayed - display the next one
2549 * unless there is another displacement coming up */
2551 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2552 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2553 pmt->hTopMenu, TRUE, wFlags);
2559 /***********************************************************************
2562 * Handle a VK_RIGHT key event in a menu.
2564 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2567 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2570 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2572 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2573 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2575 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2577 /* If already displaying a popup, try to display sub-popup */
2579 hmenutmp = pmt->hCurrentMenu;
2580 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2582 /* if subpopup was displayed then we are done */
2583 if (hmenutmp != pmt->hCurrentMenu) return;
2586 /* Check to see if there's another column */
2587 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2588 NO_SELECTED_ITEM ) {
2589 TRACE("Going to %d.\n", nextcol );
2590 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2595 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2597 if( pmt->hCurrentMenu != pmt->hTopMenu )
2599 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2600 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2601 } else hmenutmp = 0;
2603 /* try to move to the next item */
2604 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2605 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2607 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2608 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2609 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2610 pmt->hTopMenu, TRUE, wFlags);
2614 /***********************************************************************
2617 * Menu tracking code.
2619 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2620 HWND hwnd, const RECT *lprect )
2625 INT executedMenuId = -1;
2627 BOOL enterIdleSent = FALSE;
2630 mt.hCurrentMenu = hmenu;
2631 mt.hTopMenu = hmenu;
2632 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2636 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2637 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2638 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2641 if (!(menu = MENU_GetMenu( hmenu )))
2643 WARN("Invalid menu handle %p\n", hmenu);
2644 SetLastError(ERROR_INVALID_MENU_HANDLE);
2648 if (wFlags & TPM_BUTTONDOWN)
2650 /* Get the result in order to start the tracking or not */
2651 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2652 fEndMenu = !fRemove;
2655 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2657 MENU_SetCapture( mt.hOwnerWnd );
2661 menu = MENU_GetMenu( mt.hCurrentMenu );
2662 if (!menu) /* sometimes happens if I do a window manager close */
2665 /* we have to keep the message in the queue until it's
2666 * clear that menu loop is not over yet. */
2670 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2672 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2673 /* remove the message from the queue */
2674 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2680 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2681 enterIdleSent = TRUE;
2682 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2688 /* check if EndMenu() tried to cancel us, by posting this message */
2689 if(msg.message == WM_CANCELMODE)
2691 /* we are now out of the loop */
2694 /* remove the message from the queue */
2695 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2697 /* break out of internal loop, ala ESCAPE */
2701 TranslateMessage( &msg );
2704 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2705 enterIdleSent=FALSE;
2708 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2711 * Use the mouse coordinates in lParam instead of those in the MSG
2712 * struct to properly handle synthetic messages. They are already
2713 * in screen coordinates.
2715 mt.pt.x = (short)LOWORD(msg.lParam);
2716 mt.pt.y = (short)HIWORD(msg.lParam);
2718 /* Find a menu for this mouse event */
2719 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2723 /* no WM_NC... messages in captured state */
2725 case WM_RBUTTONDBLCLK:
2726 case WM_RBUTTONDOWN:
2727 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2729 case WM_LBUTTONDBLCLK:
2730 case WM_LBUTTONDOWN:
2731 /* If the message belongs to the menu, removes it from the queue */
2732 /* Else, end menu tracking */
2733 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2734 fEndMenu = !fRemove;
2738 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2741 /* Check if a menu was selected by the mouse */
2744 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2746 /* End the loop if executedMenuId is an item ID */
2747 /* or if the job was done (executedMenuId = 0). */
2748 fEndMenu = fRemove = (executedMenuId != -1);
2750 /* No menu was selected by the mouse */
2751 /* if the function was called by TrackPopupMenu, continue
2752 with the menu tracking. If not, stop it */
2754 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2759 /* the selected menu item must be changed every time */
2760 /* the mouse moves. */
2763 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2765 } /* switch(msg.message) - mouse */
2767 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2769 fRemove = TRUE; /* Keyboard messages are always removed */
2782 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2783 NO_SELECTED_ITEM, FALSE, 0 );
2786 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2787 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2790 case VK_DOWN: /* If on menu bar, pull-down the menu */
2792 menu = MENU_GetMenu( mt.hCurrentMenu );
2793 if (!(menu->wFlags & MF_POPUP))
2794 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2795 else /* otherwise try to move selection */
2796 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2800 MENU_KeyLeft( &mt, wFlags );
2804 MENU_KeyRight( &mt, wFlags );
2808 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2814 hi.cbSize = sizeof(HELPINFO);
2815 hi.iContextType = HELPINFO_MENUITEM;
2816 if (menu->FocusedItem == NO_SELECTED_ITEM)
2819 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2820 hi.hItemHandle = hmenu;
2821 hi.dwContextId = menu->dwContextHelpID;
2822 hi.MousePos = msg.pt;
2823 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2830 break; /* WM_KEYDOWN */
2837 if (msg.wParam == '\r' || msg.wParam == ' ')
2839 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2840 fEndMenu = (executedMenuId != -1);
2845 /* Hack to avoid control chars. */
2846 /* We will find a better way real soon... */
2847 if (msg.wParam < 32) break;
2849 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2850 LOWORD(msg.wParam), FALSE );
2851 if (pos == (UINT)-2) fEndMenu = TRUE;
2852 else if (pos == (UINT)-1) MessageBeep(0);
2855 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2857 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2858 fEndMenu = (executedMenuId != -1);
2862 } /* switch(msg.message) - kbd */
2866 DispatchMessageW( &msg );
2869 if (!fEndMenu) fRemove = TRUE;
2871 /* finally remove message from the queue */
2873 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2874 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2875 else mt.trackFlags &= ~TF_SKIPREMOVE;
2878 MENU_SetCapture(0); /* release the capture */
2880 /* If dropdown is still painted and the close box is clicked on
2881 then the menu will be destroyed as part of the DispatchMessage above.
2882 This will then invalidate the menu handle in mt.hTopMenu. We should
2883 check for this first. */
2884 if( IsMenu( mt.hTopMenu ) )
2886 menu = MENU_GetMenu( mt.hTopMenu );
2888 if( IsWindow( mt.hOwnerWnd ) )
2890 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2892 if (menu && (menu->wFlags & MF_POPUP))
2894 DestroyWindow( menu->hWnd );
2897 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2898 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2901 /* Reset the variable for hiding menu */
2902 if( menu ) menu->bTimeToHide = FALSE;
2905 /* The return value is only used by TrackPopupMenu */
2906 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2907 if (executedMenuId == -1) executedMenuId = 0;
2908 return executedMenuId;
2911 /***********************************************************************
2914 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2918 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2922 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2923 if (!(wFlags & TPM_NONOTIFY))
2924 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2926 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2928 if (!(wFlags & TPM_NONOTIFY))
2930 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2931 /* If an app changed/recreated menu bar entries in WM_INITMENU
2932 * menu sizes will be recalculated once the menu created/shown.
2936 /* This makes the menus of applications built with Delphi work.
2937 * It also enables menus to be displayed in more than one window,
2938 * but there are some bugs left that need to be fixed in this case.
2940 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2944 /***********************************************************************
2947 static BOOL MENU_ExitTracking(HWND hWnd)
2949 TRACE("hwnd=%p\n", hWnd);
2951 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2957 /***********************************************************************
2958 * MENU_TrackMouseMenuBar
2960 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2962 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2964 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2965 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2967 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2971 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2972 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2973 MENU_ExitTracking(hWnd);
2978 /***********************************************************************
2979 * MENU_TrackKbdMenuBar
2981 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2983 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2985 UINT uItem = NO_SELECTED_ITEM;
2987 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2989 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2991 /* find window that has a menu */
2993 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
2994 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
2996 /* check if we have to track a system menu */
2998 hTrackMenu = GetMenu( hwnd );
2999 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3001 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3002 hTrackMenu = get_win_sys_menu( hwnd );
3004 wParam |= HTSYSMENU; /* prevent item lookup */
3007 if (!IsMenu( hTrackMenu )) return;
3009 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3011 if( wChar && wChar != ' ' )
3013 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3014 if ( uItem >= (UINT)(-2) )
3016 if( uItem == (UINT)(-1) ) MessageBeep(0);
3017 /* schedule end of menu tracking */
3018 wFlags |= TF_ENDMENU;
3023 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3025 if (wParam & HTSYSMENU)
3027 /* prevent sysmenu activation for managed windows on Alt down/up */
3028 if (GetPropA( hwnd, "__wine_x11_managed" ))
3029 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3033 if( uItem == NO_SELECTED_ITEM )
3034 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3036 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3040 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3041 MENU_ExitTracking( hwnd );
3045 /**********************************************************************
3046 * TrackPopupMenu (USER32.@)
3048 * Like the win32 API, the function return the command ID only if the
3049 * flag TPM_RETURNCMD is on.
3052 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3053 INT nReserved, HWND hWnd, const RECT *lpRect )
3057 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3059 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3060 if (!(wFlags & TPM_NONOTIFY))
3061 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3063 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3064 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3065 MENU_ExitTracking(hWnd);
3070 /**********************************************************************
3071 * TrackPopupMenuEx (USER32.@)
3073 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3074 HWND hWnd, LPTPMPARAMS lpTpm )
3076 FIXME("not fully implemented\n" );
3077 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3078 lpTpm ? &lpTpm->rcExclude : NULL );
3081 /***********************************************************************
3084 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3086 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3088 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3094 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3095 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3099 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3100 return MA_NOACTIVATE;
3105 BeginPaint( hwnd, &ps );
3106 MENU_DrawPopupMenu( hwnd, ps.hdc,
3107 (HMENU)GetWindowLongW( hwnd, 0 ) );
3108 EndPaint( hwnd, &ps );
3115 /* zero out global pointer in case resident popup window was destroyed. */
3116 if (hwnd == top_popup) top_popup = 0;
3123 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3126 SetWindowLongW( hwnd, 0, 0 );
3129 case MM_SETMENUHANDLE:
3130 SetWindowLongW( hwnd, 0, wParam );
3133 case MM_GETMENUHANDLE:
3134 return GetWindowLongW( hwnd, 0 );
3137 return DefWindowProcW( hwnd, message, wParam, lParam );
3143 /***********************************************************************
3144 * MENU_GetMenuBarHeight
3146 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3148 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3149 INT orgX, INT orgY )
3155 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3157 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3159 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3160 SelectObject( hdc, hMenuFont);
3161 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3162 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3163 ReleaseDC( hwnd, hdc );
3164 return lppop->Height;
3168 /*******************************************************************
3169 * ChangeMenuA (USER32.@)
3171 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3172 UINT id, UINT flags )
3174 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3175 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3177 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3178 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3180 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3181 flags & MF_BYPOSITION ? pos : id,
3182 flags & ~MF_REMOVE );
3183 /* Default: MF_INSERT */
3184 return InsertMenuA( hMenu, pos, flags, id, data );
3188 /*******************************************************************
3189 * ChangeMenuW (USER32.@)
3191 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3192 UINT id, UINT flags )
3194 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3195 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3197 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3198 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3200 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3201 flags & MF_BYPOSITION ? pos : id,
3202 flags & ~MF_REMOVE );
3203 /* Default: MF_INSERT */
3204 return InsertMenuW( hMenu, pos, flags, id, data );
3208 /*******************************************************************
3209 * CheckMenuItem (USER32.@)
3211 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3216 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3217 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3218 ret = item->fState & MF_CHECKED;
3219 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3220 else item->fState &= ~MF_CHECKED;
3225 /**********************************************************************
3226 * EnableMenuItem (USER32.@)
3228 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3234 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3236 /* Get the Popupmenu to access the owner menu */
3237 if (!(menu = MENU_GetMenu(hMenu)))
3240 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3243 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3244 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3246 /* If the close item in the system menu change update the close button */
3247 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3249 if (menu->hSysMenuOwner != 0)
3251 POPUPMENU* parentMenu;
3253 /* Get the parent menu to access*/
3254 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3257 /* Refresh the frame to reflect the change*/
3258 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3259 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3267 /*******************************************************************
3268 * GetMenuStringA (USER32.@)
3270 INT WINAPI GetMenuStringA(
3271 HMENU hMenu, /* [in] menuhandle */
3272 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3273 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3274 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3275 UINT wFlags /* [in] MF_ flags */
3279 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3280 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3281 if (!IS_STRING_ITEM(item->fType)) return 0;
3282 if (!str || !nMaxSiz) return strlenW(item->text);
3284 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3286 TRACE("returning '%s'\n", str );
3291 /*******************************************************************
3292 * GetMenuStringW (USER32.@)
3294 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3295 LPWSTR str, INT nMaxSiz, UINT wFlags )
3299 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3300 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3301 if (!IS_STRING_ITEM(item->fType)) return 0;
3302 if (!str || !nMaxSiz) return strlenW(item->text);
3304 lstrcpynW( str, item->text, nMaxSiz );
3305 return strlenW(str);
3309 /**********************************************************************
3310 * HiliteMenuItem (USER32.@)
3312 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3316 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3317 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3318 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3319 if (menu->FocusedItem == wItemID) return TRUE;
3320 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3321 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3326 /**********************************************************************
3327 * GetMenuState (USER32.@)
3329 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3332 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3333 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3334 debug_print_menuitem (" item: ", item, "");
3335 if (item->fType & MF_POPUP)
3337 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3338 if (!menu) return -1;
3339 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3343 /* We used to (from way back then) mask the result to 0xff. */
3344 /* I don't know why and it seems wrong as the documented */
3345 /* return flag MF_SEPARATOR is outside that mask. */
3346 return (item->fType | item->fState);
3351 /**********************************************************************
3352 * GetMenuItemCount (USER32.@)
3354 INT WINAPI GetMenuItemCount( HMENU hMenu )
3356 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3357 if (!menu) return -1;
3358 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3359 return menu->nItems;
3363 /**********************************************************************
3364 * GetMenuItemID (USER32.@)
3366 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3370 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3371 if (lpmi->fType & MF_POPUP) return -1;
3377 /*******************************************************************
3378 * InsertMenuW (USER32.@)
3380 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3381 UINT_PTR id, LPCWSTR str )
3385 if (IS_STRING_ITEM(flags) && str)
3386 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3387 hMenu, pos, flags, id, debugstr_w(str) );
3388 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3389 hMenu, pos, flags, id, (DWORD)str );
3391 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3393 if (!(MENU_SetItemData( item, flags, id, str )))
3395 RemoveMenu( hMenu, pos, flags );
3399 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3400 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3402 item->hCheckBit = item->hUnCheckBit = 0;
3407 /*******************************************************************
3408 * InsertMenuA (USER32.@)
3410 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3411 UINT_PTR id, LPCSTR str )
3415 if (IS_STRING_ITEM(flags) && str)
3417 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3418 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3421 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3422 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3423 HeapFree( GetProcessHeap(), 0, newstr );
3427 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3431 /*******************************************************************
3432 * AppendMenuA (USER32.@)
3434 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3435 UINT_PTR id, LPCSTR data )
3437 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3441 /*******************************************************************
3442 * AppendMenuW (USER32.@)
3444 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3445 UINT_PTR id, LPCWSTR data )
3447 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3451 /**********************************************************************
3452 * RemoveMenu (USER32.@)
3454 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3459 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3460 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3461 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3465 MENU_FreeItemData( item );
3467 if (--menu->nItems == 0)
3469 HeapFree( GetProcessHeap(), 0, menu->items );
3474 while(nPos < menu->nItems)
3480 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3481 menu->nItems * sizeof(MENUITEM) );
3487 /**********************************************************************
3488 * DeleteMenu (USER32.@)
3490 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3492 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3493 if (!item) return FALSE;
3494 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3495 /* nPos is now the position of the item */
3496 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3501 /*******************************************************************
3502 * ModifyMenuW (USER32.@)
3504 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3505 UINT_PTR id, LPCWSTR str )
3509 if (IS_STRING_ITEM(flags))
3511 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3515 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3518 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3519 return MENU_SetItemData( item, flags, id, str );
3523 /*******************************************************************
3524 * ModifyMenuA (USER32.@)
3526 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3527 UINT_PTR id, LPCSTR str )
3531 if (IS_STRING_ITEM(flags) && str)
3533 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3534 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3537 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3538 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3539 HeapFree( GetProcessHeap(), 0, newstr );
3543 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3547 /**********************************************************************
3548 * CreatePopupMenu (USER32.@)
3550 HMENU WINAPI CreatePopupMenu(void)
3555 if (!(hmenu = CreateMenu())) return 0;
3556 menu = MENU_GetMenu( hmenu );
3557 menu->wFlags |= MF_POPUP;
3558 menu->bTimeToHide = FALSE;
3563 /**********************************************************************
3564 * GetMenuCheckMarkDimensions (USER.417)
3565 * GetMenuCheckMarkDimensions (USER32.@)
3567 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3569 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3573 /**********************************************************************
3574 * SetMenuItemBitmaps (USER32.@)
3576 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3577 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3580 TRACE("(%p, %04x, %04x, %p, %p)\n",
3581 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3582 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3584 if (!hNewCheck && !hNewUnCheck)
3586 item->fState &= ~MF_USECHECKBITMAPS;
3588 else /* Install new bitmaps */
3590 item->hCheckBit = hNewCheck;
3591 item->hUnCheckBit = hNewUnCheck;
3592 item->fState |= MF_USECHECKBITMAPS;
3598 /**********************************************************************
3599 * CreateMenu (USER32.@)
3601 HMENU WINAPI CreateMenu(void)
3605 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3606 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3608 ZeroMemory(menu, sizeof(POPUPMENU));
3609 menu->wMagic = MENU_MAGIC;
3610 menu->FocusedItem = NO_SELECTED_ITEM;
3611 menu->bTimeToHide = FALSE;
3613 TRACE("return %p\n", hMenu );
3619 /**********************************************************************
3620 * DestroyMenu (USER32.@)
3622 BOOL WINAPI DestroyMenu( HMENU hMenu )
3624 TRACE("(%p)\n", hMenu);
3626 /* Silently ignore attempts to destroy default system popup */
3628 if (hMenu && hMenu != MENU_DefSysPopup)
3630 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3632 if (!lppop) return FALSE;
3634 lppop->wMagic = 0; /* Mark it as destroyed */
3636 /* DestroyMenu should not destroy system menu popup owner */
3637 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3639 DestroyWindow( lppop->hWnd );
3643 if (lppop->items) /* recursively destroy submenus */
3646 MENUITEM *item = lppop->items;
3647 for (i = lppop->nItems; i > 0; i--, item++)
3649 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3650 MENU_FreeItemData( item );
3652 HeapFree( GetProcessHeap(), 0, lppop->items );
3654 USER_HEAP_FREE( hMenu );
3656 return (hMenu != MENU_DefSysPopup);
3660 /**********************************************************************
3661 * GetSystemMenu (USER32.@)
3663 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3665 WND *wndPtr = WIN_GetPtr( hWnd );
3668 if (wndPtr == WND_DESKTOP) return 0;
3669 if (wndPtr == WND_OTHER_PROCESS)
3671 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3675 if( wndPtr->hSysMenu )
3679 DestroyMenu(wndPtr->hSysMenu);
3680 wndPtr->hSysMenu = 0;
3684 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3687 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3688 menu->items[0].hSubMenu = MENU_CopySysPopup();
3692 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3693 wndPtr->hSysMenu, hWnd);
3694 wndPtr->hSysMenu = 0;
3699 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3700 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3702 if( wndPtr->hSysMenu )
3705 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3707 /* Store the dummy sysmenu handle to facilitate the refresh */
3708 /* of the close button if the SC_CLOSE item change */
3709 menu = MENU_GetMenu(retvalue);
3711 menu->hSysMenuOwner = wndPtr->hSysMenu;
3713 WIN_ReleasePtr( wndPtr );
3715 return bRevert ? 0 : retvalue;
3719 /*******************************************************************
3720 * SetSystemMenu (USER32.@)
3722 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3724 WND *wndPtr = WIN_GetPtr( hwnd );
3726 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3728 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3729 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3730 WIN_ReleasePtr( wndPtr );
3737 /**********************************************************************
3738 * GetMenu (USER32.@)
3740 HMENU WINAPI GetMenu( HWND hWnd )
3742 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3743 TRACE("for %p returning %p\n", hWnd, retvalue);
3748 /**********************************************************************
3751 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3752 * SetWindowPos call that would result if SetMenu were called directly.
3754 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3756 TRACE("(%p, %p);\n", hWnd, hMenu);
3758 if (hMenu && !IsMenu(hMenu))
3760 WARN("hMenu %p is not a menu handle\n", hMenu);
3763 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3766 hWnd = WIN_GetFullHandle( hWnd );
3767 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3773 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3775 lpmenu->hWnd = hWnd;
3776 lpmenu->Height = 0; /* Make sure we recalculate the size */
3778 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3783 /**********************************************************************
3784 * SetMenu (USER32.@)
3786 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3788 if(!MENU_SetMenu(hWnd, hMenu))
3791 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3792 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3797 /**********************************************************************
3798 * GetSubMenu (USER32.@)
3800 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3804 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3805 if (!(lpmi->fType & MF_POPUP)) return 0;
3806 return lpmi->hSubMenu;
3810 /**********************************************************************
3811 * DrawMenuBar (USER32.@)
3813 BOOL WINAPI DrawMenuBar( HWND hWnd )
3816 HMENU hMenu = GetMenu(hWnd);
3818 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3820 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3822 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3823 lppop->hwndOwner = hWnd;
3824 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3825 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3829 /***********************************************************************
3830 * DrawMenuBarTemp (USER32.@)
3834 * called by W98SE desk.cpl Control Panel Applet
3836 * Not 100% sure about the param names, but close.
3838 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3845 hMenu = GetMenu(hwnd);
3850 lppop = MENU_GetMenu( hMenu );
3851 if (lppop == NULL || lprect == NULL)
3853 retvalue = GetSystemMetrics(SM_CYMENU);
3857 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3859 hfontOld = SelectObject( hDC, hFont);
3861 if (lppop->Height == 0)
3862 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3864 lprect->bottom = lprect->top + lppop->Height;
3866 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3868 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3869 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3870 LineTo( hDC, lprect->right, lprect->bottom );
3872 if (lppop->nItems == 0)
3874 retvalue = GetSystemMetrics(SM_CYMENU);
3878 for (i = 0; i < lppop->nItems; i++)
3880 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3881 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3883 retvalue = lppop->Height;
3886 if (hfontOld) SelectObject (hDC, hfontOld);
3890 /***********************************************************************
3891 * EndMenu (USER.187)
3892 * EndMenu (USER32.@)
3894 void WINAPI EndMenu(void)
3896 /* if we are in the menu code, and it is active */
3897 if (!fEndMenu && top_popup)
3899 /* terminate the menu handling code */
3902 /* needs to be posted to wakeup the internal menu handler */
3903 /* which will now terminate the menu, in the event that */
3904 /* the main window was minimized, or lost focus, so we */
3905 /* don't end up with an orphaned menu */
3906 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3911 /***********************************************************************
3912 * LookupMenuHandle (USER.217)
3914 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3916 HMENU hmenu32 = HMENU_32(hmenu);
3918 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3919 else return HMENU_16(hmenu32);
3923 /**********************************************************************
3924 * LoadMenu (USER.150)
3926 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3932 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3933 if (!name) return 0;
3935 instance = GetExePtr( instance );
3936 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3937 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3938 hMenu = LoadMenuIndirect16(LockResource16(handle));
3939 FreeResource16( handle );
3944 /*****************************************************************
3945 * LoadMenuA (USER32.@)
3947 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3949 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3950 if (!hrsrc) return 0;
3951 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3955 /*****************************************************************
3956 * LoadMenuW (USER32.@)
3958 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3960 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3961 if (!hrsrc) return 0;
3962 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3966 /**********************************************************************
3967 * LoadMenuIndirect (USER.220)
3969 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3972 WORD version, offset;
3973 LPCSTR p = (LPCSTR)template;
3975 TRACE("(%p)\n", template );
3976 version = GET_WORD(p);
3980 WARN("version must be 0 for Win16\n" );
3983 offset = GET_WORD(p);
3984 p += sizeof(WORD) + offset;
3985 if (!(hMenu = CreateMenu())) return 0;
3986 if (!MENU_ParseResource( p, hMenu, FALSE ))
3988 DestroyMenu( hMenu );
3991 return HMENU_16(hMenu);
3995 /**********************************************************************
3996 * LoadMenuIndirectW (USER32.@)
3998 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4001 WORD version, offset;
4002 LPCSTR p = (LPCSTR)template;
4004 version = GET_WORD(p);
4006 TRACE("%p, ver %d\n", template, version );
4009 case 0: /* standard format is version of 0 */
4010 offset = GET_WORD(p);
4011 p += sizeof(WORD) + offset;
4012 if (!(hMenu = CreateMenu())) return 0;
4013 if (!MENU_ParseResource( p, hMenu, TRUE ))
4015 DestroyMenu( hMenu );
4019 case 1: /* extended format is version of 1 */
4020 offset = GET_WORD(p);
4021 p += sizeof(WORD) + offset;
4022 if (!(hMenu = CreateMenu())) return 0;
4023 if (!MENUEX_ParseResource( p, hMenu))
4025 DestroyMenu( hMenu );
4030 ERR("version %d not supported.\n", version);
4036 /**********************************************************************
4037 * LoadMenuIndirectA (USER32.@)
4039 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4041 return LoadMenuIndirectW( template );
4045 /**********************************************************************
4048 BOOL WINAPI IsMenu(HMENU hmenu)
4050 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4051 return menu != NULL;
4054 /**********************************************************************
4055 * GetMenuItemInfo_common
4058 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4059 LPMENUITEMINFOW lpmii, BOOL unicode)
4061 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4063 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4068 if (lpmii->fMask & MIIM_TYPE) {
4069 lpmii->fType = menu->fType;
4070 switch (MENU_ITEM_TYPE(menu->fType)) {
4072 break; /* will be done below */
4075 lpmii->dwTypeData = menu->text;
4082 /* copy the text string */
4083 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4084 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4089 len = strlenW(menu->text);
4090 if(lpmii->dwTypeData && lpmii->cch)
4091 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4095 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4096 if(lpmii->dwTypeData && lpmii->cch)
4097 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4098 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4099 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4101 /* if we've copied a substring we return its length */
4102 if(lpmii->dwTypeData && lpmii->cch)
4104 if (lpmii->cch <= len) lpmii->cch--;
4106 else /* return length of string */
4110 if (lpmii->fMask & MIIM_FTYPE)
4111 lpmii->fType = menu->fType;
4113 if (lpmii->fMask & MIIM_BITMAP)
4114 lpmii->hbmpItem = menu->hbmpItem;
4116 if (lpmii->fMask & MIIM_STATE)
4117 lpmii->fState = menu->fState;
4119 if (lpmii->fMask & MIIM_ID)
4120 lpmii->wID = menu->wID;
4122 if (lpmii->fMask & MIIM_SUBMENU)
4123 lpmii->hSubMenu = menu->hSubMenu;
4125 if (lpmii->fMask & MIIM_CHECKMARKS) {
4126 lpmii->hbmpChecked = menu->hCheckBit;
4127 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4129 if (lpmii->fMask & MIIM_DATA)
4130 lpmii->dwItemData = menu->dwItemData;
4135 /**********************************************************************
4136 * GetMenuItemInfoA (USER32.@)
4138 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4139 LPMENUITEMINFOA lpmii)
4141 return GetMenuItemInfo_common (hmenu, item, bypos,
4142 (LPMENUITEMINFOW)lpmii, FALSE);
4145 /**********************************************************************
4146 * GetMenuItemInfoW (USER32.@)
4148 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4149 LPMENUITEMINFOW lpmii)
4151 return GetMenuItemInfo_common (hmenu, item, bypos,
4156 /* set a menu item text from a ASCII or Unicode string */
4157 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4162 menu->fType |= MF_SEPARATOR;
4166 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4167 strcpyW( menu->text, text );
4171 LPCSTR str = (LPCSTR)text;
4172 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4173 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4174 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4179 /**********************************************************************
4180 * SetMenuItemInfo_common
4183 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4184 const MENUITEMINFOW *lpmii,
4187 if (!menu) return FALSE;
4189 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4191 if (lpmii->fMask & MIIM_TYPE ) {
4192 /* Get rid of old string. */
4193 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4194 HeapFree(GetProcessHeap(), 0, menu->text);
4198 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4199 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4200 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4202 menu->text = lpmii->dwTypeData;
4204 if (IS_STRING_ITEM(menu->fType))
4205 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4208 if (lpmii->fMask & MIIM_FTYPE ) {
4209 /* free the string when the type is changing */
4210 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4211 HeapFree(GetProcessHeap(), 0, menu->text);
4214 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4215 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4216 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4217 menu->fType |= MF_SEPARATOR;
4220 if (lpmii->fMask & MIIM_STRING ) {
4221 if (IS_STRING_ITEM(menu->fType)) {
4222 /* free the string when used */
4223 HeapFree(GetProcessHeap(), 0, menu->text);
4224 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4228 if (lpmii->fMask & MIIM_STATE)
4230 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4231 menu->fState = lpmii->fState;
4234 if (lpmii->fMask & MIIM_ID)
4235 menu->wID = lpmii->wID;
4237 if (lpmii->fMask & MIIM_SUBMENU) {
4238 menu->hSubMenu = lpmii->hSubMenu;
4239 if (menu->hSubMenu) {
4240 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4242 subMenu->wFlags |= MF_POPUP;
4243 menu->fType |= MF_POPUP;
4246 /* FIXME: Return an error ? */
4247 menu->fType &= ~MF_POPUP;
4250 menu->fType &= ~MF_POPUP;
4253 if (lpmii->fMask & MIIM_CHECKMARKS)
4255 if (lpmii->fType & MFT_RADIOCHECK)
4256 menu->fType |= MFT_RADIOCHECK;
4258 menu->hCheckBit = lpmii->hbmpChecked;
4259 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4261 if (lpmii->fMask & MIIM_DATA)
4262 menu->dwItemData = lpmii->dwItemData;
4264 if (lpmii->fMask & MIIM_BITMAP)
4265 menu->hbmpItem = lpmii->hbmpItem;
4267 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4271 /**********************************************************************
4272 * SetMenuItemInfoA (USER32.@)
4274 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4275 const MENUITEMINFOA *lpmii)
4277 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4278 (const MENUITEMINFOW *)lpmii, FALSE);
4281 /**********************************************************************
4282 * SetMenuItemInfoW (USER32.@)
4284 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4285 const MENUITEMINFOW *lpmii)
4287 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4291 /**********************************************************************
4292 * SetMenuDefaultItem (USER32.@)
4295 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4301 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4303 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4305 /* reset all default-item flags */
4307 for (i = 0; i < menu->nItems; i++, item++)
4309 item->fState &= ~MFS_DEFAULT;
4312 /* no default item */
4321 if ( uItem >= menu->nItems ) return FALSE;
4322 item[uItem].fState |= MFS_DEFAULT;
4327 for (i = 0; i < menu->nItems; i++, item++)
4329 if (item->wID == uItem)
4331 item->fState |= MFS_DEFAULT;
4340 /**********************************************************************
4341 * GetMenuDefaultItem (USER32.@)
4343 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4349 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4351 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4353 /* find default item */
4357 if (! item) return -1;
4359 while ( !( item->fState & MFS_DEFAULT ) )
4362 if (i >= menu->nItems ) return -1;
4365 /* default: don't return disabled items */
4366 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4368 /* search rekursiv when needed */
4369 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4372 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4373 if ( -1 != ret ) return ret;
4375 /* when item not found in submenu, return the popup item */
4377 return ( bypos ) ? i : item->wID;
4382 /**********************************************************************
4383 * InsertMenuItemA (USER32.@)
4385 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4386 const MENUITEMINFOA *lpmii)
4388 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4389 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4393 /**********************************************************************
4394 * InsertMenuItemW (USER32.@)
4396 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4397 const MENUITEMINFOW *lpmii)
4399 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4400 return SetMenuItemInfo_common(item, lpmii, TRUE);
4403 /**********************************************************************
4404 * CheckMenuRadioItem (USER32.@)
4407 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4408 UINT first, UINT last, UINT check,
4411 MENUITEM *mifirst, *milast, *micheck;
4412 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4414 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4416 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4417 milast = MENU_FindItem (&mlast, &last, bypos);
4418 micheck = MENU_FindItem (&mcheck, &check, bypos);
4420 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4421 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4422 micheck > milast || micheck < mifirst)
4425 while (mifirst <= milast)
4427 if (mifirst == micheck)
4429 mifirst->fType |= MFT_RADIOCHECK;
4430 mifirst->fState |= MFS_CHECKED;
4432 mifirst->fType &= ~MFT_RADIOCHECK;
4433 mifirst->fState &= ~MFS_CHECKED;
4442 /**********************************************************************
4443 * GetMenuItemRect (USER32.@)
4445 * ATTENTION: Here, the returned values in rect are the screen
4446 * coordinates of the item just like if the menu was
4447 * always on the upper left side of the application.
4450 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4453 POPUPMENU *itemMenu;
4457 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4459 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4460 referenceHwnd = hwnd;
4464 itemMenu = MENU_GetMenu(hMenu);
4465 if (itemMenu == NULL)
4468 if(itemMenu->hWnd == 0)
4470 referenceHwnd = itemMenu->hWnd;
4473 if ((rect == NULL) || (item == NULL))
4478 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4484 /**********************************************************************
4485 * SetMenuInfo (USER32.@)
4488 * MIM_APPLYTOSUBMENUS
4489 * actually use the items to draw the menu
4491 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4495 TRACE("(%p %p)\n", hMenu, lpmi);
4497 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4500 if (lpmi->fMask & MIM_BACKGROUND)
4501 menu->hbrBack = lpmi->hbrBack;
4503 if (lpmi->fMask & MIM_HELPID)
4504 menu->dwContextHelpID = lpmi->dwContextHelpID;
4506 if (lpmi->fMask & MIM_MAXHEIGHT)
4507 menu->cyMax = lpmi->cyMax;
4509 if (lpmi->fMask & MIM_MENUDATA)
4510 menu->dwMenuData = lpmi->dwMenuData;
4512 if (lpmi->fMask & MIM_STYLE)
4514 menu->dwStyle = lpmi->dwStyle;
4515 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4516 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4517 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4518 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4519 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4527 /**********************************************************************
4528 * GetMenuInfo (USER32.@)
4534 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4537 TRACE("(%p %p)\n", hMenu, lpmi);
4539 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4542 if (lpmi->fMask & MIM_BACKGROUND)
4543 lpmi->hbrBack = menu->hbrBack;
4545 if (lpmi->fMask & MIM_HELPID)
4546 lpmi->dwContextHelpID = menu->dwContextHelpID;
4548 if (lpmi->fMask & MIM_MAXHEIGHT)
4549 lpmi->cyMax = menu->cyMax;
4551 if (lpmi->fMask & MIM_MENUDATA)
4552 lpmi->dwMenuData = menu->dwMenuData;
4554 if (lpmi->fMask & MIM_STYLE)
4555 lpmi->dwStyle = menu->dwStyle;
4563 /**********************************************************************
4564 * SetMenuContextHelpId (USER32.@)
4566 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4570 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4572 if ((menu = MENU_GetMenu(hMenu)))
4574 menu->dwContextHelpID = dwContextHelpID;
4581 /**********************************************************************
4582 * GetMenuContextHelpId (USER32.@)
4584 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4588 TRACE("(%p)\n", hMenu);
4590 if ((menu = MENU_GetMenu(hMenu)))
4592 return menu->dwContextHelpID;
4597 /**********************************************************************
4598 * MenuItemFromPoint (USER32.@)
4600 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4602 POPUPMENU *menu = MENU_GetMenu(hMenu);
4605 /*FIXME: Do we have to handle hWnd here? */
4606 if (!menu) return -1;
4607 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4612 /**********************************************************************
4613 * translate_accelerator
4615 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4616 BYTE fVirt, WORD key, WORD cmd )
4621 if (wParam != key) return FALSE;
4623 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4624 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4625 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4627 if (message == WM_CHAR || message == WM_SYSCHAR)
4629 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4631 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4637 if(fVirt & FVIRTKEY)
4639 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4640 wParam, 0xff & HIWORD(lParam));
4642 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4643 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4647 if (!(lParam & 0x01000000)) /* no special_key */
4649 if ((fVirt & FALT) && (lParam & 0x20000000))
4650 { /* ^^ ALT pressed */
4651 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4660 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4664 HMENU hMenu, hSubMenu, hSysMenu;
4665 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4667 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4668 hSysMenu = get_win_sys_menu( hWnd );
4670 /* find menu item and ask application to initialize it */
4671 /* 1. in the system menu */
4672 hSubMenu = hSysMenu;
4674 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4678 if (!IsWindowEnabled(hWnd))
4682 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4683 if(hSubMenu != hSysMenu)
4685 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4686 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4687 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4689 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4692 else /* 2. in the window's menu */
4696 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4700 if (!IsWindowEnabled(hWnd))
4704 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4705 if(hSubMenu != hMenu)
4707 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4708 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4709 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4711 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4718 if (uSysStat != (UINT)-1)
4720 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4727 if (uStat != (UINT)-1)
4733 if (uStat & (MF_DISABLED|MF_GRAYED))
4745 if( mesg==WM_COMMAND )
4747 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4748 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4750 else if( mesg==WM_SYSCOMMAND )
4752 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4753 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4757 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4758 * #0: unknown (please report!)
4759 * #1: for WM_KEYUP,WM_SYSKEYUP
4760 * #2: mouse is captured
4761 * #3: window is disabled
4762 * #4: it's a disabled system menu option
4763 * #5: it's a menu option, but window is iconic
4764 * #6: it's a menu option, but disabled
4766 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4768 ERR_(accel)(" unknown reason - please report!\n");
4773 /**********************************************************************
4774 * TranslateAccelerator (USER32.@)
4775 * TranslateAcceleratorA (USER32.@)
4777 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4780 LPACCEL16 lpAccelTbl;
4784 if (!hWnd || !msg) return 0;
4786 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4788 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4792 wParam = msg->wParam;
4794 switch (msg->message)
4803 char ch = LOWORD(wParam);
4805 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4806 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4814 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4815 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4819 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4820 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4822 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4827 /**********************************************************************
4828 * TranslateAcceleratorW (USER32.@)
4830 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4833 LPACCEL16 lpAccelTbl;
4836 if (!hWnd || !msg) return 0;
4838 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4840 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4844 switch (msg->message)
4856 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4857 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4861 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4862 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4864 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);