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 /* Space between 2 menu bar items */
133 #define MENU_BAR_ITEMS_SPACE 12
135 /* Minimum width of a tab character */
136 #define MENU_TAB_SPACE 8
138 /* Height of a separator item */
139 #define SEPARATOR_HEIGHT 5
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* (other menu->FocusedItem values give the position of the focused item) */
145 #define NO_SELECTED_ITEM 0xffff
147 #define MENU_ITEM_TYPE(flags) \
148 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
150 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
151 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
152 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
154 #define IS_SYSTEM_MENU(menu) \
155 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
157 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
158 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
159 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
160 MF_POPUP | MF_SYSMENU | MF_HELP)
161 #define STATE_MASK (~TYPE_MASK)
163 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
165 /* Dimension of the menu bitmaps */
166 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
168 static HBITMAP hStdMnArrow = 0;
169 static HBITMAP hBmpSysMenu = 0;
171 static HFONT hMenuFont = 0;
172 static HFONT hMenuFontBold = 0;
173 static SIZE menucharsize;
174 static UINT ODitemheight; /* default owner drawn item height */
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 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
384 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
386 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
387 (UINT_PTR)hPopupMenu, NULL );
389 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
390 menu->items[0].fState = 0;
391 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
393 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
396 DestroyMenu( hMenu );
398 ERR("failed to load system menu!\n");
403 /***********************************************************************
406 * Menus initialisation.
410 NONCLIENTMETRICSW ncm;
412 /* Load menu bitmaps */
413 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
414 /* Load system buttons bitmaps */
415 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
420 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
421 arrow_bitmap_width = bm.bmWidth;
422 arrow_bitmap_height = bm.bmHeight;
426 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
429 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
430 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
433 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
436 ncm.lfMenuFont.lfWeight += 300;
437 if ( ncm.lfMenuFont.lfWeight > 1000)
438 ncm.lfMenuFont.lfWeight = 1000;
440 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
446 /***********************************************************************
447 * MENU_InitSysMenuPopup
449 * Grey the appropriate items in System menu.
451 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
455 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
456 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = ((style & WS_MAXIMIZE) != 0);
458 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
459 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
460 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
461 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
462 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
463 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
464 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
465 gray = (clsStyle & CS_NOCLOSE) != 0;
467 /* The menu item must keep its state if it's disabled */
469 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
473 /******************************************************************************
475 * UINT MENU_GetStartOfNextColumn(
478 *****************************************************************************/
480 static UINT MENU_GetStartOfNextColumn(
483 POPUPMENU *menu = MENU_GetMenu(hMenu);
487 return NO_SELECTED_ITEM;
489 i = menu->FocusedItem + 1;
490 if( i == NO_SELECTED_ITEM )
493 for( ; i < menu->nItems; ++i ) {
494 if (menu->items[i].fType & MF_MENUBARBREAK)
498 return NO_SELECTED_ITEM;
502 /******************************************************************************
504 * UINT MENU_GetStartOfPrevColumn(
507 *****************************************************************************/
509 static UINT MENU_GetStartOfPrevColumn(
512 POPUPMENU *menu = MENU_GetMenu(hMenu);
516 return NO_SELECTED_ITEM;
518 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
519 return NO_SELECTED_ITEM;
521 /* Find the start of the column */
523 for(i = menu->FocusedItem; i != 0 &&
524 !(menu->items[i].fType & MF_MENUBARBREAK);
528 return NO_SELECTED_ITEM;
530 for(--i; i != 0; --i) {
531 if (menu->items[i].fType & MF_MENUBARBREAK)
535 TRACE("ret %d.\n", i );
542 /***********************************************************************
545 * Find a menu item. Return a pointer on the item, and modifies *hmenu
546 * in case the item was in a sub-menu.
548 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
553 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
554 if (wFlags & MF_BYPOSITION)
556 if (*nPos >= menu->nItems) return NULL;
557 return &menu->items[*nPos];
561 MENUITEM *item = menu->items;
562 for (i = 0; i < menu->nItems; i++, item++)
564 if (item->wID == *nPos)
569 else if (item->fType & MF_POPUP)
571 HMENU hsubmenu = item->hSubMenu;
572 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
584 /***********************************************************************
587 * Find a Sub menu. Return the position of the submenu, and modifies
588 * *hmenu in case it is found in another sub-menu.
589 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
591 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
596 if (((*hmenu)==(HMENU)0xffff) ||
597 (!(menu = MENU_GetMenu(*hmenu))))
598 return NO_SELECTED_ITEM;
600 for (i = 0; i < menu->nItems; i++, item++) {
601 if(!(item->fType & MF_POPUP)) continue;
602 if (item->hSubMenu == hSubTarget) {
606 HMENU hsubmenu = item->hSubMenu;
607 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
608 if (pos != NO_SELECTED_ITEM) {
614 return NO_SELECTED_ITEM;
617 /***********************************************************************
620 static void MENU_FreeItemData( MENUITEM* item )
623 if (IS_STRING_ITEM(item->fType) && item->text)
624 HeapFree( GetProcessHeap(), 0, item->text );
627 /***********************************************************************
628 * MENU_FindItemByCoords
630 * Find the item at the specified coordinates (screen coords). Does
631 * not work for child windows and therefore should not be called for
632 * an arbitrary system menu.
634 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
635 POINT pt, UINT *pos )
641 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
642 pt.x -= wrect.left;pt.y -= wrect.top;
644 for (i = 0; i < menu->nItems; i++, item++)
646 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
647 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
657 /***********************************************************************
660 * Find the menu item selected by a key press.
661 * Return item id, -1 if none, -2 if we should close the menu.
663 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
664 WCHAR key, BOOL forceMenuChar )
666 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
668 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
672 POPUPMENU *menu = MENU_GetMenu( hmenu );
673 MENUITEM *item = menu->items;
680 for (i = 0; i < menu->nItems; i++, item++)
682 if (IS_STRING_ITEM(item->fType) && item->text)
684 WCHAR *p = item->text - 2;
687 p = strchrW (p + 2, '&');
689 while (p != NULL && p [1] == '&');
690 if (p && (toupperW(p[1]) == toupperW(key))) return i;
694 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
695 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
696 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
697 if (HIWORD(menuchar) == 1) return (UINT)(-2);
703 /***********************************************************************
704 * MENU_GetBitmapItemSize
706 * Get the size of a bitmap item.
708 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
711 HBITMAP bmp = (HBITMAP)id;
713 size->cx = size->cy = 0;
715 /* check if there is a magic menu item associated with this item */
716 if (id && IS_MAGIC_ITEM( id ))
720 case (INT_PTR)HBMMENU_SYSTEM:
727 case (INT_PTR)HBMMENU_MBAR_RESTORE:
728 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
729 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
730 case (INT_PTR)HBMMENU_MBAR_CLOSE:
731 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
732 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
735 case (INT_PTR)HBMMENU_CALLBACK:
736 case (INT_PTR)HBMMENU_POPUP_CLOSE:
737 case (INT_PTR)HBMMENU_POPUP_RESTORE:
738 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
739 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
741 FIXME("Magic 0x%08x not implemented\n", id);
745 if (GetObjectW(bmp, sizeof(bm), &bm ))
747 size->cx = bm.bmWidth;
748 size->cy = bm.bmHeight;
752 /***********************************************************************
753 * MENU_DrawBitmapItem
755 * Draw a bitmap item.
756 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
758 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
763 HBITMAP bmp = (HBITMAP)lpitem->text;
764 int w = rect->right - rect->left;
765 int h = rect->bottom - rect->top;
768 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
770 /* Check if there is a magic menu item associated with this item */
771 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
776 switch(LOWORD(hbmToDraw))
778 case (INT_PTR)HBMMENU_SYSTEM:
779 if (lpitem->dwItemData)
781 bmp = (HBITMAP)lpitem->dwItemData;
782 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
787 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
788 /* only use right half of the bitmap */
789 bmp_xoffset = bm.bmWidth / 2;
790 bm.bmWidth -= bmp_xoffset;
793 case (INT_PTR)HBMMENU_MBAR_RESTORE:
794 flags = DFCS_CAPTIONRESTORE;
796 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
797 flags = DFCS_CAPTIONMIN;
799 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
800 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
802 case (INT_PTR)HBMMENU_MBAR_CLOSE:
803 flags = DFCS_CAPTIONCLOSE;
805 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
806 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
808 case (INT_PTR)HBMMENU_CALLBACK:
809 case (INT_PTR)HBMMENU_POPUP_CLOSE:
810 case (INT_PTR)HBMMENU_POPUP_RESTORE:
811 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
812 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
814 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
818 InflateRect( &r, -1, -1 );
819 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
820 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
824 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
827 hdcMem = CreateCompatibleDC( hdc );
828 SelectObject( hdcMem, bmp );
830 /* handle fontsize > bitmap_height */
831 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
833 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
834 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
835 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
836 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
841 /***********************************************************************
844 * Calculate the size of the menu item and store it in lpitem->rect.
846 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
847 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
850 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
852 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
853 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
854 (menuBar ? " (MenuBar)" : ""));
856 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
858 if (lpitem->fType & MF_OWNERDRAW)
860 MEASUREITEMSTRUCT mis;
861 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
862 if( !menucharsize.cx ) {
863 DIALOG_GetCharSize( hdc, hMenuFont, &menucharsize );
864 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
865 * but it is unlikely an application will depend on that */
866 ODitemheight = HIWORD( GetDialogBaseUnits());
868 mis.CtlType = ODT_MENU;
870 mis.itemID = lpitem->wID;
871 mis.itemData = (DWORD)lpitem->dwItemData;
872 mis.itemHeight = ODitemheight;
874 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
875 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
876 * width of a menufont character to the width of an owner-drawn menu.
878 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
880 /* under at least win95 you seem to be given a standard
881 height for the menu and the height value is ignored */
882 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
884 lpitem->rect.bottom += mis.itemHeight;
886 TRACE("id=%04x size=%ldx%ld\n",
887 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
888 lpitem->rect.bottom-lpitem->rect.top);
892 if (lpitem->fType & MF_SEPARATOR)
894 lpitem->rect.bottom += SEPARATOR_HEIGHT;
900 /* New style MIIM_BITMAP */
901 if (lpitem->hbmpItem)
903 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
905 MEASUREITEMSTRUCT measItem;
906 measItem.CtlType = ODT_MENU;
908 measItem.itemID = lpitem->wID;
909 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
910 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
911 measItem.itemData = lpitem->dwItemData;
913 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
915 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
916 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
917 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
918 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
921 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
922 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
923 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
924 lpitem->rect.right += size.cx;
925 lpitem->rect.bottom += size.cy;
927 if (lppop->dwStyle & MNS_CHECKORBMP)
928 lpitem->rect.right += check_bitmap_width;
930 lpitem->rect.right += 2 * check_bitmap_width;
932 lpitem->rect.right += 2 * check_bitmap_width;
933 if (lpitem->fType & MF_POPUP)
934 lpitem->rect.right += arrow_bitmap_width;
937 if (IS_BITMAP_ITEM(lpitem->fType))
941 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
942 lpitem->rect.right += size.cx;
943 lpitem->rect.bottom += size.cy;
944 /* Leave space for the sunken border */
945 lpitem->rect.right += 2;
946 lpitem->rect.bottom += 2;
950 /* it must be a text item - unless it's the system menu */
951 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
954 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
956 lpitem->rect.right += size.cx;
957 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
962 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
964 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
966 /* Item contains a tab (only meaningful in popup menus) */
967 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
968 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
969 lpitem->rect.right += MENU_TAB_SPACE;
973 if (strchrW( lpitem->text, '\b' ))
974 lpitem->rect.right += MENU_TAB_SPACE;
975 lpitem->xTab = lpitem->rect.right - check_bitmap_width
976 - arrow_bitmap_width;
979 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
983 /***********************************************************************
984 * MENU_PopupMenuCalcSize
986 * Calculate the size of a popup menu.
988 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
993 int orgX, orgY, maxX, maxTab, maxTabWidth;
995 lppop->Width = lppop->Height = 0;
996 if (lppop->nItems == 0) return;
999 SelectObject( hdc, hMenuFont);
1004 lppop->maxBmpSize.cx = 0;
1005 lppop->maxBmpSize.cy = 0;
1007 while (start < lppop->nItems)
1009 lpitem = &lppop->items[start];
1011 if( lpitem->fType & MF_MENUBREAK)
1012 orgX += MENU_COL_SPACE;
1015 maxTab = maxTabWidth = 0;
1016 /* Parse items until column break or end of menu */
1017 for (i = start; i < lppop->nItems; i++, lpitem++)
1020 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1022 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1024 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1025 maxX = max( maxX, lpitem->rect.right );
1026 orgY = lpitem->rect.bottom;
1027 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1029 maxTab = max( maxTab, lpitem->xTab );
1030 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1034 /* Finish the column (set all items to the largest width found) */
1035 maxX = max( maxX, maxTab + maxTabWidth );
1036 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1038 lpitem->rect.right = maxX;
1039 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1040 lpitem->xTab = maxTab;
1043 lppop->Height = max( lppop->Height, orgY );
1046 lppop->Width = maxX;
1048 /* space for 3d border */
1052 ReleaseDC( 0, hdc );
1056 /***********************************************************************
1057 * MENU_MenuBarCalcSize
1059 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1060 * height is off by 1 pixel which causes lengthy window relocations when
1061 * active document window is maximized/restored.
1063 * Calculate the size of the menu bar.
1065 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1066 LPPOPUPMENU lppop, HWND hwndOwner )
1069 int start, i, orgX, orgY, maxY, helpPos;
1071 if ((lprect == NULL) || (lppop == NULL)) return;
1072 if (lppop->nItems == 0) return;
1073 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1074 lprect->left, lprect->top, lprect->right, lprect->bottom);
1075 lppop->Width = lprect->right - lprect->left;
1077 maxY = lprect->top+1;
1080 lppop->maxBmpSize.cx = 0;
1081 lppop->maxBmpSize.cy = 0;
1082 while (start < lppop->nItems)
1084 lpitem = &lppop->items[start];
1085 orgX = lprect->left;
1088 /* Parse items until line break or end of menu */
1089 for (i = start; i < lppop->nItems; i++, lpitem++)
1091 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1093 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1095 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1097 debug_print_menuitem (" item: ", lpitem, "");
1098 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1100 if (lpitem->rect.right > lprect->right)
1102 if (i != start) break;
1103 else lpitem->rect.right = lprect->right;
1105 maxY = max( maxY, lpitem->rect.bottom );
1106 orgX = lpitem->rect.right;
1109 /* Finish the line (set all items to the largest height found) */
1110 while (start < i) lppop->items[start++].rect.bottom = maxY;
1113 lprect->bottom = maxY;
1114 lppop->Height = lprect->bottom - lprect->top;
1116 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1117 /* the last item (if several lines, only move the last line) */
1118 lpitem = &lppop->items[lppop->nItems-1];
1119 orgY = lpitem->rect.top;
1120 orgX = lprect->right;
1121 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1122 if ( (helpPos==-1) || (helpPos>i) )
1124 if (lpitem->rect.top != orgY) break; /* Other line */
1125 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1126 lpitem->rect.left += orgX - lpitem->rect.right;
1127 lpitem->rect.right = orgX;
1128 orgX = lpitem->rect.left;
1132 /***********************************************************************
1135 * Draw a single menu item.
1137 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1138 UINT height, BOOL menuBar, UINT odaction )
1141 BOOL flat_menu = FALSE;
1144 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1146 if (lpitem->fType & MF_SYSMENU)
1148 if( !IsIconic(hwnd) )
1149 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1153 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1154 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1158 if (lpitem->fState & MF_HILITE)
1160 if(menuBar && !flat_menu) {
1161 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1162 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1164 if(lpitem->fState & MF_GRAYED)
1165 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1167 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1168 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1173 if (lpitem->fState & MF_GRAYED)
1174 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1176 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1177 SetBkColor( hdc, GetSysColor( bkgnd ) );
1180 if (lpitem->fType & MF_OWNERDRAW)
1183 ** Experimentation under Windows reveals that an owner-drawn
1184 ** menu is given the rectangle which includes the space it requested
1185 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1186 ** and a popup-menu arrow. This is the value of lpitem->rect.
1187 ** Windows will leave all drawing to the application except for
1188 ** the popup-menu arrow. Windows always draws that itself, after
1189 ** the menu owner has finished drawing.
1193 dis.CtlType = ODT_MENU;
1195 dis.itemID = lpitem->wID;
1196 dis.itemData = (DWORD)lpitem->dwItemData;
1198 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1199 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1200 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1201 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1202 dis.hwndItem = (HWND)hmenu;
1204 dis.rcItem = lpitem->rect;
1205 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1206 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1207 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1208 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1210 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1211 /* Fall through to draw popup-menu arrow */
1214 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1215 lpitem->rect.right,lpitem->rect.bottom);
1217 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1219 rect = lpitem->rect;
1221 if (!(lpitem->fType & MF_OWNERDRAW))
1223 if (lpitem->fState & MF_HILITE)
1227 InflateRect (&rect, -1, -1);
1228 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1229 InflateRect (&rect, 1, 1);
1230 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1235 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1237 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1241 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1244 SetBkMode( hdc, TRANSPARENT );
1246 if (!(lpitem->fType & MF_OWNERDRAW))
1250 /* vertical separator */
1251 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1255 rc.bottom = height - 3;
1258 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1259 MoveToEx( hdc, rc.left, rc.top, NULL );
1260 LineTo( hdc, rc.left, rc.bottom );
1261 SelectObject( hdc, oldPen );
1264 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1267 /* horizontal separator */
1268 if (lpitem->fType & MF_SEPARATOR)
1273 rc.top += SEPARATOR_HEIGHT / 2;
1276 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1277 MoveToEx( hdc, rc.left, rc.top, NULL );
1278 LineTo( hdc, rc.right, rc.top );
1279 SelectObject( hdc, oldPen );
1282 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1287 /* helper lines for debugging */
1288 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1289 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1290 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1291 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1297 INT y = rect.top + rect.bottom;
1298 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1299 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1301 if (!(lpitem->fType & MF_OWNERDRAW))
1305 /* New style MIIM_BITMAP */
1306 if (lpitem->hbmpItem)
1308 POPUPMENU *menu = MENU_GetMenu(hmenu);
1309 if (menu->dwStyle & MNS_CHECKORBMP)
1310 rc.left += menu->maxBmpSize.cx - check_bitmap_width;
1312 rc.left += menu->maxBmpSize.cx;
1314 /* Draw the check mark
1317 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1319 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1320 if (bm) /* we have a custom bitmap */
1322 HDC hdcMem = CreateCompatibleDC( hdc );
1323 SelectObject( hdcMem, bm );
1324 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1325 check_bitmap_width, check_bitmap_height,
1326 hdcMem, 0, 0, SRCCOPY );
1329 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1332 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1333 HDC hdcMem = CreateCompatibleDC( hdc );
1334 SelectObject( hdcMem, bm );
1335 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1336 DrawFrameControl( hdcMem, &r, DFC_MENU,
1337 (lpitem->fType & MFT_RADIOCHECK) ?
1338 DFCS_MENUBULLET : DFCS_MENUCHECK );
1339 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1340 hdcMem, 0, 0, SRCCOPY );
1344 /* New style MIIM_BITMAP */
1345 if (lpitem->hbmpItem)
1347 HBITMAP hbm = lpitem->hbmpItem;
1349 if (hbm == HBMMENU_CALLBACK)
1351 DRAWITEMSTRUCT drawItem;
1353 drawItem.CtlType = ODT_MENU;
1355 drawItem.itemID = lpitem->wID;
1356 drawItem.itemAction = odaction;
1357 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1358 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1359 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1360 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1361 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1362 drawItem.hwndItem = (HWND)hmenu;
1364 drawItem.rcItem = lpitem->rect;
1365 drawItem.itemData = lpitem->dwItemData;
1366 /* some applications make this assumption on the DC's origin */
1367 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1368 OffsetRect( &drawItem.rcItem, - lpitem->rect.left, - lpitem->rect.top);
1369 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1370 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1373 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1378 /* Draw the popup-menu arrow */
1379 if (lpitem->fType & MF_POPUP)
1381 HDC hdcMem = CreateCompatibleDC( hdc );
1382 HBITMAP hOrigBitmap;
1384 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1385 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1386 (y - arrow_bitmap_height) / 2,
1387 arrow_bitmap_width, arrow_bitmap_height,
1388 hdcMem, 0, 0, SRCCOPY );
1389 SelectObject( hdcMem, hOrigBitmap );
1393 rect.left += check_bitmap_width;
1394 rect.right -= arrow_bitmap_width;
1397 /* Done for owner-drawn */
1398 if (lpitem->fType & MF_OWNERDRAW)
1401 /* Draw the item text or bitmap */
1402 if (IS_BITMAP_ITEM(lpitem->fType))
1404 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1407 /* No bitmap - process text if present */
1408 else if (IS_STRING_ITEM(lpitem->fType))
1413 UINT uFormat = (menuBar) ?
1414 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1415 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1417 if ( lpitem->fState & MFS_DEFAULT )
1419 hfontOld = SelectObject( hdc, hMenuFontBold);
1424 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1425 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1428 for (i = 0; lpitem->text[i]; i++)
1429 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1432 if(lpitem->fState & MF_GRAYED)
1434 if (!(lpitem->fState & MF_HILITE) )
1436 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1437 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1438 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1439 --rect.left; --rect.top; --rect.right; --rect.bottom;
1441 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1444 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1446 /* paint the shortcut text */
1447 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1449 if (lpitem->text[i] == '\t')
1451 rect.left = lpitem->xTab;
1452 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1456 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1459 if(lpitem->fState & MF_GRAYED)
1461 if (!(lpitem->fState & MF_HILITE) )
1463 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1464 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1465 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1466 --rect.left; --rect.top; --rect.right; --rect.bottom;
1468 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1470 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1474 SelectObject (hdc, hfontOld);
1479 /***********************************************************************
1480 * MENU_DrawPopupMenu
1482 * Paint a popup menu.
1484 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1486 HBRUSH hPrevBrush = 0;
1489 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1491 GetClientRect( hwnd, &rect );
1493 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1494 && (SelectObject( hdc, hMenuFont)))
1498 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1500 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1504 BOOL flat_menu = FALSE;
1506 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1508 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1510 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1512 /* draw menu items */
1514 menu = MENU_GetMenu( hmenu );
1515 if (menu && menu->nItems)
1520 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1521 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1522 menu->Height, FALSE, ODA_DRAWENTIRE );
1527 SelectObject( hdc, hPrevBrush );
1532 /***********************************************************************
1535 * Paint a menu bar. Returns the height of the menu bar.
1536 * called from [windows/nonclient.c]
1538 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1543 HMENU hMenu = GetMenu(hwnd);
1545 lppop = MENU_GetMenu( hMenu );
1546 if (lppop == NULL || lprect == NULL)
1548 return GetSystemMetrics(SM_CYMENU);
1553 hfontOld = SelectObject( hDC, hMenuFont);
1555 if (lppop->Height == 0)
1556 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1558 lprect->bottom = lprect->top + lppop->Height;
1560 if (hfontOld) SelectObject( hDC, hfontOld);
1561 return lppop->Height;
1564 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1568 /***********************************************************************
1571 * Display a popup menu.
1573 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1574 INT x, INT y, INT xanchor, INT yanchor )
1579 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1580 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1582 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1583 if (menu->FocusedItem != NO_SELECTED_ITEM)
1585 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1586 menu->FocusedItem = NO_SELECTED_ITEM;
1589 /* store the owner for DrawItem */
1590 menu->hwndOwner = hwndOwner;
1592 MENU_PopupMenuCalcSize( menu, hwndOwner );
1594 /* adjust popup menu pos so that it fits within the desktop */
1596 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1597 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1599 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1602 x -= width - xanchor;
1603 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1604 x = GetSystemMetrics(SM_CXSCREEN) - width;
1608 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1611 y -= height + yanchor;
1612 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1613 y = GetSystemMetrics(SM_CYSCREEN) - height;
1617 /* NOTE: In Windows, top menu popup is not owned. */
1618 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1619 WS_POPUP, x, y, width, height,
1620 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1622 if( !menu->hWnd ) return FALSE;
1623 if (!top_popup) top_popup = menu->hWnd;
1625 /* Display the window */
1627 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1628 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1629 UpdateWindow( menu->hWnd );
1634 /***********************************************************************
1637 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1638 BOOL sendMenuSelect, HMENU topmenu )
1643 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1645 lppop = MENU_GetMenu( hmenu );
1646 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1648 if (lppop->FocusedItem == wIndex) return;
1649 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1650 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1651 if (!top_popup) top_popup = lppop->hWnd;
1653 SelectObject( hdc, hMenuFont);
1655 /* Clear previous highlighted item */
1656 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1658 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1659 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1660 lppop->Height, !(lppop->wFlags & MF_POPUP),
1664 /* Highlight new item (if any) */
1665 lppop->FocusedItem = wIndex;
1666 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1668 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1669 lppop->items[wIndex].fState |= MF_HILITE;
1670 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1671 &lppop->items[wIndex], lppop->Height,
1672 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1676 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1677 SendMessageW( hwndOwner, WM_MENUSELECT,
1678 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1679 ip->fType | ip->fState |
1680 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1683 else if (sendMenuSelect) {
1686 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1687 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1688 MENUITEM *ip = &ptm->items[pos];
1689 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1690 ip->fType | ip->fState |
1691 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1695 ReleaseDC( lppop->hWnd, hdc );
1699 /***********************************************************************
1700 * MENU_MoveSelection
1702 * Moves currently selected item according to the offset parameter.
1703 * If there is no selection then it should select the last item if
1704 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1706 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1711 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1713 menu = MENU_GetMenu( hmenu );
1714 if ((!menu) || (!menu->items)) return;
1716 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1718 if( menu->nItems == 1 ) return; else
1719 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1721 if (!(menu->items[i].fType & MF_SEPARATOR))
1723 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1728 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1729 i >= 0 && i < menu->nItems ; i += offset)
1730 if (!(menu->items[i].fType & MF_SEPARATOR))
1732 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1738 /**********************************************************************
1741 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1744 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1747 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1749 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1750 TRACE("flags=%x str=%p\n", flags, str);
1752 if (IS_STRING_ITEM(flags))
1756 flags |= MF_SEPARATOR;
1762 /* Item beginning with a backspace is a help item */
1768 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1770 strcpyW( text, str );
1774 else if (IS_BITMAP_ITEM(flags))
1775 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1776 else item->text = NULL;
1778 if (flags & MF_OWNERDRAW)
1779 item->dwItemData = (DWORD)str;
1781 item->dwItemData = 0;
1783 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1784 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1786 if (flags & MF_POPUP)
1788 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1789 if (menu) menu->wFlags |= MF_POPUP;
1801 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1803 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1804 flags |= MF_POPUP; /* keep popup */
1806 item->fType = flags & TYPE_MASK;
1807 item->fState = (flags & STATE_MASK) &
1808 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1811 /* Don't call SetRectEmpty here! */
1814 HeapFree( GetProcessHeap(), 0, prevText );
1816 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1821 /**********************************************************************
1824 * Insert (allocate) a new item into a menu.
1826 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1831 if (!(menu = MENU_GetMenu(hMenu)))
1834 /* Find where to insert new item */
1836 if (flags & MF_BYPOSITION) {
1837 if (pos > menu->nItems)
1840 if (!MENU_FindItem( &hMenu, &pos, flags ))
1843 if (!(menu = MENU_GetMenu( hMenu )))
1848 /* Create new items array */
1850 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1853 WARN("allocation failed\n" );
1856 if (menu->nItems > 0)
1858 /* Copy the old array into the new one */
1859 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1860 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1861 (menu->nItems-pos)*sizeof(MENUITEM) );
1862 HeapFree( GetProcessHeap(), 0, menu->items );
1864 menu->items = newItems;
1866 memset( &newItems[pos], 0, sizeof(*newItems) );
1867 menu->Height = 0; /* force size recalculate */
1868 return &newItems[pos];
1872 /**********************************************************************
1873 * MENU_ParseResource
1875 * Parse a standard menu resource and add items to the menu.
1876 * Return a pointer to the end of the resource.
1878 * NOTE: flags is equivalent to the mtOption field
1880 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1887 flags = GET_WORD(res);
1888 res += sizeof(WORD);
1889 if (!(flags & MF_POPUP))
1892 res += sizeof(WORD);
1895 if (!unicode) res += strlen(str) + 1;
1896 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1897 if (flags & MF_POPUP)
1899 HMENU hSubMenu = CreatePopupMenu();
1900 if (!hSubMenu) return NULL;
1901 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1903 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1904 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1906 else /* Not a popup */
1908 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1909 else AppendMenuW( hMenu, flags, id,
1910 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1912 } while (!(flags & MF_END));
1917 /**********************************************************************
1918 * MENUEX_ParseResource
1920 * Parse an extended menu resource and add items to the menu.
1921 * Return a pointer to the end of the resource.
1923 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1929 mii.cbSize = sizeof(mii);
1930 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1931 mii.fType = GET_DWORD(res);
1932 res += sizeof(DWORD);
1933 mii.fState = GET_DWORD(res);
1934 res += sizeof(DWORD);
1935 mii.wID = GET_DWORD(res);
1936 res += sizeof(DWORD);
1937 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1938 res += sizeof(WORD);
1939 /* Align the text on a word boundary. */
1940 res += (~((int)res - 1)) & 1;
1941 mii.dwTypeData = (LPWSTR) res;
1942 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1943 /* Align the following fields on a dword boundary. */
1944 res += (~((int)res - 1)) & 3;
1946 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1947 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1949 if (resinfo & 1) { /* Pop-up? */
1950 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1951 res += sizeof(DWORD);
1952 mii.hSubMenu = CreatePopupMenu();
1955 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1956 DestroyMenu(mii.hSubMenu);
1959 mii.fMask |= MIIM_SUBMENU;
1960 mii.fType |= MF_POPUP;
1962 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1964 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1965 mii.wID, mii.fType);
1966 mii.fType |= MF_SEPARATOR;
1968 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1969 } while (!(resinfo & MF_END));
1974 /***********************************************************************
1977 * Return the handle of the selected sub-popup menu (if any).
1979 static HMENU MENU_GetSubPopup( HMENU hmenu )
1984 menu = MENU_GetMenu( hmenu );
1986 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1988 item = &menu->items[menu->FocusedItem];
1989 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1990 return item->hSubMenu;
1995 /***********************************************************************
1996 * MENU_HideSubPopups
1998 * Hide the sub-popup menus of this menu.
2000 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2001 BOOL sendMenuSelect )
2003 POPUPMENU *menu = MENU_GetMenu( hmenu );
2005 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2007 if (menu && top_popup)
2013 if (menu->FocusedItem != NO_SELECTED_ITEM)
2015 item = &menu->items[menu->FocusedItem];
2016 if (!(item->fType & MF_POPUP) ||
2017 !(item->fState & MF_MOUSESELECT)) return;
2018 item->fState &= ~MF_MOUSESELECT;
2019 hsubmenu = item->hSubMenu;
2022 submenu = MENU_GetMenu( hsubmenu );
2023 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2024 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2025 DestroyWindow( submenu->hWnd );
2031 /***********************************************************************
2034 * Display the sub-menu of the selected item of this menu.
2035 * Return the handle of the submenu, or hmenu if no submenu to display.
2037 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2038 BOOL selectFirst, UINT wFlags )
2045 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2047 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2049 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2051 item = &menu->items[menu->FocusedItem];
2052 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2055 /* message must be sent before using item,
2056 because nearly everything may be changed by the application ! */
2058 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2059 if (!(wFlags & TPM_NONOTIFY))
2060 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2061 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2063 item = &menu->items[menu->FocusedItem];
2066 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2067 if (!(item->fState & MF_HILITE))
2069 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2070 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2072 SelectObject( hdc, hMenuFont);
2074 item->fState |= MF_HILITE;
2075 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2076 ReleaseDC( menu->hWnd, hdc );
2078 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2081 item->fState |= MF_MOUSESELECT;
2083 if (IS_SYSTEM_MENU(menu))
2085 MENU_InitSysMenuPopup(item->hSubMenu,
2086 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2087 GetClassLongW( menu->hWnd, GCL_STYLE));
2089 NC_GetSysPopupPos( menu->hWnd, &rect );
2090 rect.top = rect.bottom;
2091 rect.right = GetSystemMetrics(SM_CXSIZE);
2092 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2096 GetWindowRect( menu->hWnd, &rect );
2097 if (menu->wFlags & MF_POPUP)
2099 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2100 rect.top += item->rect.top;
2101 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2102 rect.bottom = item->rect.top - item->rect.bottom;
2106 rect.left += item->rect.left;
2107 rect.top += item->rect.bottom;
2108 rect.right = item->rect.right - item->rect.left;
2109 rect.bottom = item->rect.bottom - item->rect.top;
2113 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2114 rect.left, rect.top, rect.right, rect.bottom );
2116 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2117 return item->hSubMenu;
2122 /**********************************************************************
2125 HWND MENU_IsMenuActive(void)
2130 /***********************************************************************
2133 * Walks menu chain trying to find a menu pt maps to.
2135 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2137 POPUPMENU *menu = MENU_GetMenu( hMenu );
2138 UINT item = menu->FocusedItem;
2141 /* try subpopup first (if any) */
2142 ret = (item != NO_SELECTED_ITEM &&
2143 (menu->items[item].fType & MF_POPUP) &&
2144 (menu->items[item].fState & MF_MOUSESELECT))
2145 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2147 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2149 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2150 if( menu->wFlags & MF_POPUP )
2152 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2154 else if (ht == HTSYSMENU)
2155 ret = get_win_sys_menu( menu->hWnd );
2156 else if (ht == HTMENU)
2157 ret = GetMenu( menu->hWnd );
2162 /***********************************************************************
2163 * MENU_ExecFocusedItem
2165 * Execute a menu item (for instance when user pressed Enter).
2166 * Return the wID of the executed item. Otherwise, -1 indicating
2167 * that no menu item was executed;
2168 * Have to receive the flags for the TrackPopupMenu options to avoid
2169 * sending unwanted message.
2172 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2175 POPUPMENU *menu = MENU_GetMenu( hMenu );
2177 TRACE("%p hmenu=%p\n", pmt, hMenu);
2179 if (!menu || !menu->nItems ||
2180 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2182 item = &menu->items[menu->FocusedItem];
2184 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2186 if (!(item->fType & MF_POPUP))
2188 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2190 /* If TPM_RETURNCMD is set you return the id, but
2191 do not send a message to the owner */
2192 if(!(wFlags & TPM_RETURNCMD))
2194 if( menu->wFlags & MF_SYSMENU )
2195 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2196 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2198 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2204 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2209 /***********************************************************************
2210 * MENU_SwitchTracking
2212 * Helper function for menu navigation routines.
2214 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2216 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2217 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2219 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2221 if( pmt->hTopMenu != hPtMenu &&
2222 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2224 /* both are top level menus (system and menu-bar) */
2225 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2226 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2227 pmt->hTopMenu = hPtMenu;
2229 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2230 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2234 /***********************************************************************
2237 * Return TRUE if we can go on with menu tracking.
2239 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2241 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2246 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2249 if( IS_SYSTEM_MENU(ptmenu) )
2250 item = ptmenu->items;
2252 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2256 if( ptmenu->FocusedItem != id )
2257 MENU_SwitchTracking( pmt, hPtMenu, id );
2259 /* If the popup menu is not already "popped" */
2260 if(!(item->fState & MF_MOUSESELECT ))
2262 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2267 /* Else the click was on the menu bar, finish the tracking */
2272 /***********************************************************************
2275 * Return the value of MENU_ExecFocusedItem if
2276 * the selected item was not a popup. Else open the popup.
2277 * A -1 return value indicates that we go on with menu tracking.
2280 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2282 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2287 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2290 if( IS_SYSTEM_MENU(ptmenu) )
2291 item = ptmenu->items;
2293 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2295 if( item && (ptmenu->FocusedItem == id ))
2297 if( !(item->fType & MF_POPUP) )
2298 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2300 /* If we are dealing with the top-level menu */
2301 /* and this is a click on an already "popped" item: */
2302 /* Stop the menu tracking and close the opened submenus */
2303 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2306 ptmenu->bTimeToHide = TRUE;
2312 /***********************************************************************
2315 * Return TRUE if we can go on with menu tracking.
2317 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2319 UINT id = NO_SELECTED_ITEM;
2320 POPUPMENU *ptmenu = NULL;
2324 ptmenu = MENU_GetMenu( hPtMenu );
2325 if( IS_SYSTEM_MENU(ptmenu) )
2328 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2331 if( id == NO_SELECTED_ITEM )
2333 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2334 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2337 else if( ptmenu->FocusedItem != id )
2339 MENU_SwitchTracking( pmt, hPtMenu, id );
2340 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2346 /***********************************************************************
2349 static void MENU_SetCapture( HWND hwnd )
2353 SERVER_START_REQ( set_capture_window )
2356 req->flags = CAPTURE_MENU;
2357 if (!wine_server_call_err( req ))
2359 previous = reply->previous;
2360 hwnd = reply->full_handle;
2365 if (previous && previous != hwnd)
2366 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2370 /***********************************************************************
2373 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2375 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2377 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2379 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2380 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2382 MDINEXTMENU next_menu;
2387 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2388 next_menu.hmenuNext = 0;
2389 next_menu.hwndNext = 0;
2390 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2392 TRACE("%p [%p] -> %p [%p]\n",
2393 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2395 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2397 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2398 hNewWnd = pmt->hOwnerWnd;
2399 if( IS_SYSTEM_MENU(menu) )
2401 /* switch to the menu bar */
2403 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2407 menu = MENU_GetMenu( hNewMenu );
2408 id = menu->nItems - 1;
2411 else if (style & WS_SYSMENU )
2413 /* switch to the system menu */
2414 hNewMenu = get_win_sys_menu( hNewWnd );
2418 else /* application returned a new menu to switch to */
2420 hNewMenu = next_menu.hmenuNext;
2421 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2423 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2425 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2427 if (style & WS_SYSMENU &&
2428 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2430 /* get the real system menu */
2431 hNewMenu = get_win_sys_menu(hNewWnd);
2433 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2435 /* FIXME: Not sure what to do here;
2436 * perhaps try to track hNewMenu as a popup? */
2438 TRACE(" -- got confused.\n");
2445 if( hNewMenu != pmt->hTopMenu )
2447 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2449 if( pmt->hCurrentMenu != pmt->hTopMenu )
2450 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2453 if( hNewWnd != pmt->hOwnerWnd )
2455 pmt->hOwnerWnd = hNewWnd;
2456 MENU_SetCapture( pmt->hOwnerWnd );
2459 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2460 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2467 /***********************************************************************
2470 * The idea is not to show the popup if the next input message is
2471 * going to hide it anyway.
2473 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2477 msg.hwnd = pmt->hOwnerWnd;
2479 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2480 pmt->trackFlags |= TF_SKIPREMOVE;
2485 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2486 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2488 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2489 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2490 if( msg.message == WM_KEYDOWN &&
2491 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2493 pmt->trackFlags |= TF_SUSPENDPOPUP;
2500 /* failures go through this */
2501 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2505 /***********************************************************************
2508 * Handle a VK_ESCAPE key event in a menu.
2510 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2512 BOOL bEndMenu = TRUE;
2514 if (pmt->hCurrentMenu != pmt->hTopMenu)
2516 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2518 if (menu->wFlags & MF_POPUP)
2520 HMENU hmenutmp, hmenuprev;
2522 hmenuprev = hmenutmp = pmt->hTopMenu;
2524 /* close topmost popup */
2525 while (hmenutmp != pmt->hCurrentMenu)
2527 hmenuprev = hmenutmp;
2528 hmenutmp = MENU_GetSubPopup( hmenuprev );
2531 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2532 pmt->hCurrentMenu = hmenuprev;
2540 /***********************************************************************
2543 * Handle a VK_LEFT key event in a menu.
2545 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2548 HMENU hmenutmp, hmenuprev;
2551 hmenuprev = hmenutmp = pmt->hTopMenu;
2552 menu = MENU_GetMenu( hmenutmp );
2554 /* Try to move 1 column left (if possible) */
2555 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2556 NO_SELECTED_ITEM ) {
2558 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2563 /* close topmost popup */
2564 while (hmenutmp != pmt->hCurrentMenu)
2566 hmenuprev = hmenutmp;
2567 hmenutmp = MENU_GetSubPopup( hmenuprev );
2570 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2571 pmt->hCurrentMenu = hmenuprev;
2573 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2575 /* move menu bar selection if no more popups are left */
2577 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2578 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2580 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2582 /* A sublevel menu was displayed - display the next one
2583 * unless there is another displacement coming up */
2585 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2586 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2587 pmt->hTopMenu, TRUE, wFlags);
2593 /***********************************************************************
2596 * Handle a VK_RIGHT key event in a menu.
2598 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2601 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2604 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2606 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2607 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2609 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2611 /* If already displaying a popup, try to display sub-popup */
2613 hmenutmp = pmt->hCurrentMenu;
2614 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2616 /* if subpopup was displayed then we are done */
2617 if (hmenutmp != pmt->hCurrentMenu) return;
2620 /* Check to see if there's another column */
2621 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2622 NO_SELECTED_ITEM ) {
2623 TRACE("Going to %d.\n", nextcol );
2624 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2629 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2631 if( pmt->hCurrentMenu != pmt->hTopMenu )
2633 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2634 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2635 } else hmenutmp = 0;
2637 /* try to move to the next item */
2638 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2639 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2641 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2642 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2643 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2644 pmt->hTopMenu, TRUE, wFlags);
2648 /***********************************************************************
2651 * Menu tracking code.
2653 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2654 HWND hwnd, const RECT *lprect )
2659 INT executedMenuId = -1;
2661 BOOL enterIdleSent = FALSE;
2664 mt.hCurrentMenu = hmenu;
2665 mt.hTopMenu = hmenu;
2666 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2670 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2671 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2672 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2675 if (!(menu = MENU_GetMenu( hmenu )))
2677 WARN("Invalid menu handle %p\n", hmenu);
2678 SetLastError(ERROR_INVALID_MENU_HANDLE);
2682 if (wFlags & TPM_BUTTONDOWN)
2684 /* Get the result in order to start the tracking or not */
2685 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2686 fEndMenu = !fRemove;
2689 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2691 MENU_SetCapture( mt.hOwnerWnd );
2695 menu = MENU_GetMenu( mt.hCurrentMenu );
2696 if (!menu) /* sometimes happens if I do a window manager close */
2699 /* we have to keep the message in the queue until it's
2700 * clear that menu loop is not over yet. */
2704 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2706 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2707 /* remove the message from the queue */
2708 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2714 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2715 enterIdleSent = TRUE;
2716 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2722 /* check if EndMenu() tried to cancel us, by posting this message */
2723 if(msg.message == WM_CANCELMODE)
2725 /* we are now out of the loop */
2728 /* remove the message from the queue */
2729 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2731 /* break out of internal loop, ala ESCAPE */
2735 TranslateMessage( &msg );
2738 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2739 enterIdleSent=FALSE;
2742 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2745 * Use the mouse coordinates in lParam instead of those in the MSG
2746 * struct to properly handle synthetic messages. They are already
2747 * in screen coordinates.
2749 mt.pt.x = (short)LOWORD(msg.lParam);
2750 mt.pt.y = (short)HIWORD(msg.lParam);
2752 /* Find a menu for this mouse event */
2753 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2757 /* no WM_NC... messages in captured state */
2759 case WM_RBUTTONDBLCLK:
2760 case WM_RBUTTONDOWN:
2761 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2763 case WM_LBUTTONDBLCLK:
2764 case WM_LBUTTONDOWN:
2765 /* If the message belongs to the menu, removes it from the queue */
2766 /* Else, end menu tracking */
2767 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2768 fEndMenu = !fRemove;
2772 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2775 /* Check if a menu was selected by the mouse */
2778 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2780 /* End the loop if executedMenuId is an item ID */
2781 /* or if the job was done (executedMenuId = 0). */
2782 fEndMenu = fRemove = (executedMenuId != -1);
2784 /* No menu was selected by the mouse */
2785 /* if the function was called by TrackPopupMenu, continue
2786 with the menu tracking. If not, stop it */
2788 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2793 /* the selected menu item must be changed every time */
2794 /* the mouse moves. */
2797 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2799 } /* switch(msg.message) - mouse */
2801 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2803 fRemove = TRUE; /* Keyboard messages are always removed */
2816 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2817 NO_SELECTED_ITEM, FALSE, 0 );
2820 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2821 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2824 case VK_DOWN: /* If on menu bar, pull-down the menu */
2826 menu = MENU_GetMenu( mt.hCurrentMenu );
2827 if (!(menu->wFlags & MF_POPUP))
2828 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2829 else /* otherwise try to move selection */
2830 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2834 MENU_KeyLeft( &mt, wFlags );
2838 MENU_KeyRight( &mt, wFlags );
2842 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2848 hi.cbSize = sizeof(HELPINFO);
2849 hi.iContextType = HELPINFO_MENUITEM;
2850 if (menu->FocusedItem == NO_SELECTED_ITEM)
2853 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2854 hi.hItemHandle = hmenu;
2855 hi.dwContextId = menu->dwContextHelpID;
2856 hi.MousePos = msg.pt;
2857 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2864 break; /* WM_KEYDOWN */
2871 if (msg.wParam == '\r' || msg.wParam == ' ')
2873 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2874 fEndMenu = (executedMenuId != -1);
2879 /* Hack to avoid control chars. */
2880 /* We will find a better way real soon... */
2881 if (msg.wParam < 32) break;
2883 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2884 LOWORD(msg.wParam), FALSE );
2885 if (pos == (UINT)-2) fEndMenu = TRUE;
2886 else if (pos == (UINT)-1) MessageBeep(0);
2889 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2891 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2892 fEndMenu = (executedMenuId != -1);
2896 } /* switch(msg.message) - kbd */
2900 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2901 DispatchMessageW( &msg );
2905 if (!fEndMenu) fRemove = TRUE;
2907 /* finally remove message from the queue */
2909 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2910 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2911 else mt.trackFlags &= ~TF_SKIPREMOVE;
2914 MENU_SetCapture(0); /* release the capture */
2916 /* If dropdown is still painted and the close box is clicked on
2917 then the menu will be destroyed as part of the DispatchMessage above.
2918 This will then invalidate the menu handle in mt.hTopMenu. We should
2919 check for this first. */
2920 if( IsMenu( mt.hTopMenu ) )
2922 menu = MENU_GetMenu( mt.hTopMenu );
2924 if( IsWindow( mt.hOwnerWnd ) )
2926 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2928 if (menu && (menu->wFlags & MF_POPUP))
2930 DestroyWindow( menu->hWnd );
2933 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2934 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2937 /* Reset the variable for hiding menu */
2938 if( menu ) menu->bTimeToHide = FALSE;
2941 /* The return value is only used by TrackPopupMenu */
2942 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2943 if (executedMenuId == -1) executedMenuId = 0;
2944 return executedMenuId;
2947 /***********************************************************************
2950 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2954 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2958 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2959 if (!(wFlags & TPM_NONOTIFY))
2960 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2962 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2964 if (!(wFlags & TPM_NONOTIFY))
2966 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2967 /* If an app changed/recreated menu bar entries in WM_INITMENU
2968 * menu sizes will be recalculated once the menu created/shown.
2972 /* This makes the menus of applications built with Delphi work.
2973 * It also enables menus to be displayed in more than one window,
2974 * but there are some bugs left that need to be fixed in this case.
2976 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2980 /***********************************************************************
2983 static BOOL MENU_ExitTracking(HWND hWnd)
2985 TRACE("hwnd=%p\n", hWnd);
2987 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2993 /***********************************************************************
2994 * MENU_TrackMouseMenuBar
2996 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2998 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3000 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3001 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3003 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3007 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3008 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3009 MENU_ExitTracking(hWnd);
3014 /***********************************************************************
3015 * MENU_TrackKbdMenuBar
3017 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3019 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3021 UINT uItem = NO_SELECTED_ITEM;
3023 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3025 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3027 /* find window that has a menu */
3029 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3030 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3032 /* check if we have to track a system menu */
3034 hTrackMenu = GetMenu( hwnd );
3035 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3037 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3038 hTrackMenu = get_win_sys_menu( hwnd );
3040 wParam |= HTSYSMENU; /* prevent item lookup */
3043 if (!IsMenu( hTrackMenu )) return;
3045 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3047 if( wChar && wChar != ' ' )
3049 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3050 if ( uItem >= (UINT)(-2) )
3052 if( uItem == (UINT)(-1) ) MessageBeep(0);
3053 /* schedule end of menu tracking */
3054 wFlags |= TF_ENDMENU;
3059 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3061 if (wParam & HTSYSMENU)
3063 /* prevent sysmenu activation for managed windows on Alt down/up */
3064 if (GetPropA( hwnd, "__wine_x11_managed" ))
3065 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3069 if( uItem == NO_SELECTED_ITEM )
3070 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3072 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3076 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3077 MENU_ExitTracking( hwnd );
3081 /**********************************************************************
3082 * TrackPopupMenu (USER32.@)
3084 * Like the win32 API, the function return the command ID only if the
3085 * flag TPM_RETURNCMD is on.
3088 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3089 INT nReserved, HWND hWnd, const RECT *lpRect )
3093 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3095 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3096 if (!(wFlags & TPM_NONOTIFY))
3097 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3099 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3100 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3101 MENU_ExitTracking(hWnd);
3106 /**********************************************************************
3107 * TrackPopupMenuEx (USER32.@)
3109 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3110 HWND hWnd, LPTPMPARAMS lpTpm )
3112 FIXME("not fully implemented\n" );
3113 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3114 lpTpm ? &lpTpm->rcExclude : NULL );
3117 /***********************************************************************
3120 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3122 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3124 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3130 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3131 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3135 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3136 return MA_NOACTIVATE;
3141 BeginPaint( hwnd, &ps );
3142 MENU_DrawPopupMenu( hwnd, ps.hdc,
3143 (HMENU)GetWindowLongW( hwnd, 0 ) );
3144 EndPaint( hwnd, &ps );
3151 /* zero out global pointer in case resident popup window was destroyed. */
3152 if (hwnd == top_popup) top_popup = 0;
3159 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3162 SetWindowLongW( hwnd, 0, 0 );
3165 case MM_SETMENUHANDLE:
3166 SetWindowLongW( hwnd, 0, wParam );
3169 case MM_GETMENUHANDLE:
3170 return GetWindowLongW( hwnd, 0 );
3173 return DefWindowProcW( hwnd, message, wParam, lParam );
3179 /***********************************************************************
3180 * MENU_GetMenuBarHeight
3182 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3184 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3185 INT orgX, INT orgY )
3191 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3193 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3195 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3196 SelectObject( hdc, hMenuFont);
3197 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3198 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3199 ReleaseDC( hwnd, hdc );
3200 return lppop->Height;
3204 /*******************************************************************
3205 * ChangeMenuA (USER32.@)
3207 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3208 UINT id, UINT flags )
3210 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3211 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3213 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3214 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3216 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3217 flags & MF_BYPOSITION ? pos : id,
3218 flags & ~MF_REMOVE );
3219 /* Default: MF_INSERT */
3220 return InsertMenuA( hMenu, pos, flags, id, data );
3224 /*******************************************************************
3225 * ChangeMenuW (USER32.@)
3227 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3228 UINT id, UINT flags )
3230 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3231 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3233 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3234 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3236 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3237 flags & MF_BYPOSITION ? pos : id,
3238 flags & ~MF_REMOVE );
3239 /* Default: MF_INSERT */
3240 return InsertMenuW( hMenu, pos, flags, id, data );
3244 /*******************************************************************
3245 * CheckMenuItem (USER32.@)
3247 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3252 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3253 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3254 ret = item->fState & MF_CHECKED;
3255 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3256 else item->fState &= ~MF_CHECKED;
3261 /**********************************************************************
3262 * EnableMenuItem (USER32.@)
3264 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3270 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3272 /* Get the Popupmenu to access the owner menu */
3273 if (!(menu = MENU_GetMenu(hMenu)))
3276 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3279 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3280 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3282 /* If the close item in the system menu change update the close button */
3283 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3285 if (menu->hSysMenuOwner != 0)
3288 POPUPMENU* parentMenu;
3290 /* Get the parent menu to access*/
3291 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3294 /* Refresh the frame to reflect the change */
3295 GetWindowRect(parentMenu->hWnd, &rc);
3296 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3298 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3306 /*******************************************************************
3307 * GetMenuStringA (USER32.@)
3309 INT WINAPI GetMenuStringA(
3310 HMENU hMenu, /* [in] menuhandle */
3311 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3312 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3313 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3314 UINT wFlags /* [in] MF_ flags */
3318 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3319 if (str && nMaxSiz) str[0] = '\0';
3320 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3321 if (!IS_STRING_ITEM(item->fType)) return 0;
3322 if (!str || !nMaxSiz) return strlenW(item->text);
3323 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3325 TRACE("returning '%s'\n", str );
3330 /*******************************************************************
3331 * GetMenuStringW (USER32.@)
3333 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3334 LPWSTR str, INT nMaxSiz, UINT wFlags )
3338 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3339 if (str && nMaxSiz) str[0] = '\0';
3340 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3341 if (!IS_STRING_ITEM(item->fType)) return 0;
3342 if (!str || !nMaxSiz) return strlenW(item->text);
3343 lstrcpynW( str, item->text, nMaxSiz );
3344 return strlenW(str);
3348 /**********************************************************************
3349 * HiliteMenuItem (USER32.@)
3351 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3355 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3356 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3357 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3358 if (menu->FocusedItem == wItemID) return TRUE;
3359 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3360 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3365 /**********************************************************************
3366 * GetMenuState (USER32.@)
3368 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3371 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3372 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3373 debug_print_menuitem (" item: ", item, "");
3374 if (item->fType & MF_POPUP)
3376 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3377 if (!menu) return -1;
3378 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3382 /* We used to (from way back then) mask the result to 0xff. */
3383 /* I don't know why and it seems wrong as the documented */
3384 /* return flag MF_SEPARATOR is outside that mask. */
3385 return (item->fType | item->fState);
3390 /**********************************************************************
3391 * GetMenuItemCount (USER32.@)
3393 INT WINAPI GetMenuItemCount( HMENU hMenu )
3395 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3396 if (!menu) return -1;
3397 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3398 return menu->nItems;
3402 /**********************************************************************
3403 * GetMenuItemID (USER32.@)
3405 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3409 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3410 if (lpmi->fType & MF_POPUP) return -1;
3416 /*******************************************************************
3417 * InsertMenuW (USER32.@)
3419 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3420 UINT_PTR id, LPCWSTR str )
3424 if (IS_STRING_ITEM(flags) && str)
3425 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3426 hMenu, pos, flags, id, debugstr_w(str) );
3427 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3428 hMenu, pos, flags, id, (DWORD)str );
3430 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3432 if (!(MENU_SetItemData( item, flags, id, str )))
3434 RemoveMenu( hMenu, pos, flags );
3438 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3439 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3441 item->hCheckBit = item->hUnCheckBit = 0;
3446 /*******************************************************************
3447 * InsertMenuA (USER32.@)
3449 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3450 UINT_PTR id, LPCSTR str )
3454 if (IS_STRING_ITEM(flags) && str)
3456 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3457 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3460 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3461 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3462 HeapFree( GetProcessHeap(), 0, newstr );
3466 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3470 /*******************************************************************
3471 * AppendMenuA (USER32.@)
3473 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3474 UINT_PTR id, LPCSTR data )
3476 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3480 /*******************************************************************
3481 * AppendMenuW (USER32.@)
3483 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3484 UINT_PTR id, LPCWSTR data )
3486 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3490 /**********************************************************************
3491 * RemoveMenu (USER32.@)
3493 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3498 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3499 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3500 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3504 MENU_FreeItemData( item );
3506 if (--menu->nItems == 0)
3508 HeapFree( GetProcessHeap(), 0, menu->items );
3513 while(nPos < menu->nItems)
3519 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3520 menu->nItems * sizeof(MENUITEM) );
3526 /**********************************************************************
3527 * DeleteMenu (USER32.@)
3529 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3531 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3532 if (!item) return FALSE;
3533 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3534 /* nPos is now the position of the item */
3535 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3540 /*******************************************************************
3541 * ModifyMenuW (USER32.@)
3543 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3544 UINT_PTR id, LPCWSTR str )
3548 if (IS_STRING_ITEM(flags))
3550 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3554 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3557 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3558 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3559 return MENU_SetItemData( item, flags, id, str );
3563 /*******************************************************************
3564 * ModifyMenuA (USER32.@)
3566 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3567 UINT_PTR id, LPCSTR str )
3571 if (IS_STRING_ITEM(flags) && str)
3573 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3574 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3577 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3578 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3579 HeapFree( GetProcessHeap(), 0, newstr );
3583 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3587 /**********************************************************************
3588 * CreatePopupMenu (USER32.@)
3590 HMENU WINAPI CreatePopupMenu(void)
3595 if (!(hmenu = CreateMenu())) return 0;
3596 menu = MENU_GetMenu( hmenu );
3597 menu->wFlags |= MF_POPUP;
3598 menu->bTimeToHide = FALSE;
3603 /**********************************************************************
3604 * GetMenuCheckMarkDimensions (USER.417)
3605 * GetMenuCheckMarkDimensions (USER32.@)
3607 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3609 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3613 /**********************************************************************
3614 * SetMenuItemBitmaps (USER32.@)
3616 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3617 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3620 TRACE("(%p, %04x, %04x, %p, %p)\n",
3621 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3622 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3624 if (!hNewCheck && !hNewUnCheck)
3626 item->fState &= ~MF_USECHECKBITMAPS;
3628 else /* Install new bitmaps */
3630 item->hCheckBit = hNewCheck;
3631 item->hUnCheckBit = hNewUnCheck;
3632 item->fState |= MF_USECHECKBITMAPS;
3638 /**********************************************************************
3639 * CreateMenu (USER32.@)
3641 HMENU WINAPI CreateMenu(void)
3645 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3646 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3648 ZeroMemory(menu, sizeof(POPUPMENU));
3649 menu->wMagic = MENU_MAGIC;
3650 menu->FocusedItem = NO_SELECTED_ITEM;
3651 menu->bTimeToHide = FALSE;
3653 TRACE("return %p\n", hMenu );
3659 /**********************************************************************
3660 * DestroyMenu (USER32.@)
3662 BOOL WINAPI DestroyMenu( HMENU hMenu )
3664 TRACE("(%p)\n", hMenu);
3666 /* Silently ignore attempts to destroy default system popup */
3668 if (hMenu && hMenu != MENU_DefSysPopup)
3670 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3672 if (!lppop) return FALSE;
3674 lppop->wMagic = 0; /* Mark it as destroyed */
3676 /* DestroyMenu should not destroy system menu popup owner */
3677 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3679 DestroyWindow( lppop->hWnd );
3683 if (lppop->items) /* recursively destroy submenus */
3686 MENUITEM *item = lppop->items;
3687 for (i = lppop->nItems; i > 0; i--, item++)
3689 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3690 MENU_FreeItemData( item );
3692 HeapFree( GetProcessHeap(), 0, lppop->items );
3694 USER_HEAP_FREE( hMenu );
3696 return (hMenu != MENU_DefSysPopup);
3700 /**********************************************************************
3701 * GetSystemMenu (USER32.@)
3703 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3705 WND *wndPtr = WIN_GetPtr( hWnd );
3708 if (wndPtr == WND_DESKTOP) return 0;
3709 if (wndPtr == WND_OTHER_PROCESS)
3711 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3715 if( wndPtr->hSysMenu )
3719 DestroyMenu(wndPtr->hSysMenu);
3720 wndPtr->hSysMenu = 0;
3724 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3727 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3728 menu->items[0].hSubMenu = MENU_CopySysPopup();
3732 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3733 wndPtr->hSysMenu, hWnd);
3734 wndPtr->hSysMenu = 0;
3739 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3740 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3742 if( wndPtr->hSysMenu )
3745 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3747 /* Store the dummy sysmenu handle to facilitate the refresh */
3748 /* of the close button if the SC_CLOSE item change */
3749 menu = MENU_GetMenu(retvalue);
3751 menu->hSysMenuOwner = wndPtr->hSysMenu;
3753 WIN_ReleasePtr( wndPtr );
3755 return bRevert ? 0 : retvalue;
3759 /*******************************************************************
3760 * SetSystemMenu (USER32.@)
3762 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3764 WND *wndPtr = WIN_GetPtr( hwnd );
3766 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3768 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3769 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3770 WIN_ReleasePtr( wndPtr );
3777 /**********************************************************************
3778 * GetMenu (USER32.@)
3780 HMENU WINAPI GetMenu( HWND hWnd )
3782 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3783 TRACE("for %p returning %p\n", hWnd, retvalue);
3787 /**********************************************************************
3788 * GetMenuBarInfo (USER32.@)
3790 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3792 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
3796 /**********************************************************************
3799 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3800 * SetWindowPos call that would result if SetMenu were called directly.
3802 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3804 TRACE("(%p, %p);\n", hWnd, hMenu);
3806 if (hMenu && !IsMenu(hMenu))
3808 WARN("hMenu %p is not a menu handle\n", hMenu);
3811 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3814 hWnd = WIN_GetFullHandle( hWnd );
3815 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3821 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3823 lpmenu->hWnd = hWnd;
3824 lpmenu->Height = 0; /* Make sure we recalculate the size */
3826 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3831 /**********************************************************************
3832 * SetMenu (USER32.@)
3834 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3836 if(!MENU_SetMenu(hWnd, hMenu))
3839 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3840 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3845 /**********************************************************************
3846 * GetSubMenu (USER32.@)
3848 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3852 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3853 if (!(lpmi->fType & MF_POPUP)) return 0;
3854 return lpmi->hSubMenu;
3858 /**********************************************************************
3859 * DrawMenuBar (USER32.@)
3861 BOOL WINAPI DrawMenuBar( HWND hWnd )
3864 HMENU hMenu = GetMenu(hWnd);
3866 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3868 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3870 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3871 lppop->hwndOwner = hWnd;
3872 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3873 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3877 /***********************************************************************
3878 * DrawMenuBarTemp (USER32.@)
3882 * called by W98SE desk.cpl Control Panel Applet
3884 * Not 100% sure about the param names, but close.
3886 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3891 BOOL flat_menu = FALSE;
3893 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
3896 hMenu = GetMenu(hwnd);
3901 lppop = MENU_GetMenu( hMenu );
3902 if (lppop == NULL || lprect == NULL)
3904 retvalue = GetSystemMetrics(SM_CYMENU);
3908 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3910 hfontOld = SelectObject( hDC, hFont);
3912 if (lppop->Height == 0)
3913 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3915 lprect->bottom = lprect->top + lppop->Height;
3917 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
3919 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3920 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3921 LineTo( hDC, lprect->right, lprect->bottom );
3923 if (lppop->nItems == 0)
3925 retvalue = GetSystemMetrics(SM_CYMENU);
3929 for (i = 0; i < lppop->nItems; i++)
3931 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3932 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3934 retvalue = lppop->Height;
3937 if (hfontOld) SelectObject (hDC, hfontOld);
3941 /***********************************************************************
3942 * EndMenu (USER.187)
3943 * EndMenu (USER32.@)
3945 void WINAPI EndMenu(void)
3947 /* if we are in the menu code, and it is active */
3948 if (!fEndMenu && top_popup)
3950 /* terminate the menu handling code */
3953 /* needs to be posted to wakeup the internal menu handler */
3954 /* which will now terminate the menu, in the event that */
3955 /* the main window was minimized, or lost focus, so we */
3956 /* don't end up with an orphaned menu */
3957 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3962 /***********************************************************************
3963 * LookupMenuHandle (USER.217)
3965 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3967 HMENU hmenu32 = HMENU_32(hmenu);
3969 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3970 else return HMENU_16(hmenu32);
3974 /**********************************************************************
3975 * LoadMenu (USER.150)
3977 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3983 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3984 if (!name) return 0;
3986 instance = GetExePtr( instance );
3987 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3988 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3989 hMenu = LoadMenuIndirect16(LockResource16(handle));
3990 FreeResource16( handle );
3995 /*****************************************************************
3996 * LoadMenuA (USER32.@)
3998 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4000 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4001 if (!hrsrc) return 0;
4002 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4006 /*****************************************************************
4007 * LoadMenuW (USER32.@)
4009 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4011 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4012 if (!hrsrc) return 0;
4013 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4017 /**********************************************************************
4018 * LoadMenuIndirect (USER.220)
4020 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4023 WORD version, offset;
4024 LPCSTR p = (LPCSTR)template;
4026 TRACE("(%p)\n", template );
4027 version = GET_WORD(p);
4031 WARN("version must be 0 for Win16\n" );
4034 offset = GET_WORD(p);
4035 p += sizeof(WORD) + offset;
4036 if (!(hMenu = CreateMenu())) return 0;
4037 if (!MENU_ParseResource( p, hMenu, FALSE ))
4039 DestroyMenu( hMenu );
4042 return HMENU_16(hMenu);
4046 /**********************************************************************
4047 * LoadMenuIndirectW (USER32.@)
4049 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4052 WORD version, offset;
4053 LPCSTR p = (LPCSTR)template;
4055 version = GET_WORD(p);
4057 TRACE("%p, ver %d\n", template, version );
4060 case 0: /* standard format is version of 0 */
4061 offset = GET_WORD(p);
4062 p += sizeof(WORD) + offset;
4063 if (!(hMenu = CreateMenu())) return 0;
4064 if (!MENU_ParseResource( p, hMenu, TRUE ))
4066 DestroyMenu( hMenu );
4070 case 1: /* extended format is version of 1 */
4071 offset = GET_WORD(p);
4072 p += sizeof(WORD) + offset;
4073 if (!(hMenu = CreateMenu())) return 0;
4074 if (!MENUEX_ParseResource( p, hMenu))
4076 DestroyMenu( hMenu );
4081 ERR("version %d not supported.\n", version);
4087 /**********************************************************************
4088 * LoadMenuIndirectA (USER32.@)
4090 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4092 return LoadMenuIndirectW( template );
4096 /**********************************************************************
4099 BOOL WINAPI IsMenu(HMENU hmenu)
4101 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4102 return menu != NULL;
4105 /**********************************************************************
4106 * GetMenuItemInfo_common
4109 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4110 LPMENUITEMINFOW lpmii, BOOL unicode)
4112 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4114 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4119 if (lpmii->fMask & MIIM_TYPE) {
4120 lpmii->fType = menu->fType;
4121 switch (MENU_ITEM_TYPE(menu->fType)) {
4123 break; /* will be done below */
4126 lpmii->dwTypeData = menu->text;
4133 /* copy the text string */
4134 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4135 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4140 len = strlenW(menu->text);
4141 if(lpmii->dwTypeData && lpmii->cch)
4142 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4146 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4147 if(lpmii->dwTypeData && lpmii->cch)
4148 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4149 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4150 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4152 /* if we've copied a substring we return its length */
4153 if(lpmii->dwTypeData && lpmii->cch)
4155 if (lpmii->cch <= len) lpmii->cch--;
4157 else /* return length of string */
4161 if (lpmii->fMask & MIIM_FTYPE)
4162 lpmii->fType = menu->fType;
4164 if (lpmii->fMask & MIIM_BITMAP)
4165 lpmii->hbmpItem = menu->hbmpItem;
4167 if (lpmii->fMask & MIIM_STATE)
4168 lpmii->fState = menu->fState;
4170 if (lpmii->fMask & MIIM_ID)
4171 lpmii->wID = menu->wID;
4173 if (lpmii->fMask & MIIM_SUBMENU)
4174 lpmii->hSubMenu = menu->hSubMenu;
4176 if (lpmii->fMask & MIIM_CHECKMARKS) {
4177 lpmii->hbmpChecked = menu->hCheckBit;
4178 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4180 if (lpmii->fMask & MIIM_DATA)
4181 lpmii->dwItemData = menu->dwItemData;
4186 /**********************************************************************
4187 * GetMenuItemInfoA (USER32.@)
4189 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4190 LPMENUITEMINFOA lpmii)
4192 return GetMenuItemInfo_common (hmenu, item, bypos,
4193 (LPMENUITEMINFOW)lpmii, FALSE);
4196 /**********************************************************************
4197 * GetMenuItemInfoW (USER32.@)
4199 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4200 LPMENUITEMINFOW lpmii)
4202 return GetMenuItemInfo_common (hmenu, item, bypos,
4207 /* set a menu item text from a ASCII or Unicode string */
4208 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4213 menu->fType |= MF_SEPARATOR;
4217 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4218 strcpyW( menu->text, text );
4222 LPCSTR str = (LPCSTR)text;
4223 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4224 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4225 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4230 /**********************************************************************
4231 * SetMenuItemInfo_common
4234 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4235 const MENUITEMINFOW *lpmii,
4238 if (!menu) return FALSE;
4240 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4242 if (lpmii->fMask & MIIM_TYPE ) {
4243 /* Get rid of old string. */
4244 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4245 HeapFree(GetProcessHeap(), 0, menu->text);
4249 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4250 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4251 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4253 menu->text = lpmii->dwTypeData;
4255 if (IS_STRING_ITEM(menu->fType))
4256 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4259 if (lpmii->fMask & MIIM_FTYPE ) {
4260 /* free the string when the type is changing */
4261 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4262 HeapFree(GetProcessHeap(), 0, menu->text);
4265 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4266 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4267 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4268 menu->fType |= MF_SEPARATOR;
4271 if (lpmii->fMask & MIIM_STRING ) {
4272 if (IS_STRING_ITEM(menu->fType)) {
4273 /* free the string when used */
4274 HeapFree(GetProcessHeap(), 0, menu->text);
4275 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4279 if (lpmii->fMask & MIIM_STATE)
4281 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4282 menu->fState = lpmii->fState;
4285 if (lpmii->fMask & MIIM_ID)
4286 menu->wID = lpmii->wID;
4288 if (lpmii->fMask & MIIM_SUBMENU) {
4289 menu->hSubMenu = lpmii->hSubMenu;
4290 if (menu->hSubMenu) {
4291 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4293 subMenu->wFlags |= MF_POPUP;
4294 menu->fType |= MF_POPUP;
4297 /* FIXME: Return an error ? */
4298 menu->fType &= ~MF_POPUP;
4301 menu->fType &= ~MF_POPUP;
4304 if (lpmii->fMask & MIIM_CHECKMARKS)
4306 if (lpmii->fType & MFT_RADIOCHECK)
4307 menu->fType |= MFT_RADIOCHECK;
4309 menu->hCheckBit = lpmii->hbmpChecked;
4310 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4312 if (lpmii->fMask & MIIM_DATA)
4313 menu->dwItemData = lpmii->dwItemData;
4315 if (lpmii->fMask & MIIM_BITMAP)
4316 menu->hbmpItem = lpmii->hbmpItem;
4318 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4322 /**********************************************************************
4323 * SetMenuItemInfoA (USER32.@)
4325 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4326 const MENUITEMINFOA *lpmii)
4328 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4329 (const MENUITEMINFOW *)lpmii, FALSE);
4332 /**********************************************************************
4333 * SetMenuItemInfoW (USER32.@)
4335 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4336 const MENUITEMINFOW *lpmii)
4338 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4342 /**********************************************************************
4343 * SetMenuDefaultItem (USER32.@)
4346 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4352 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4354 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4356 /* reset all default-item flags */
4358 for (i = 0; i < menu->nItems; i++, item++)
4360 item->fState &= ~MFS_DEFAULT;
4363 /* no default item */
4372 if ( uItem >= menu->nItems ) return FALSE;
4373 item[uItem].fState |= MFS_DEFAULT;
4378 for (i = 0; i < menu->nItems; i++, item++)
4380 if (item->wID == uItem)
4382 item->fState |= MFS_DEFAULT;
4391 /**********************************************************************
4392 * GetMenuDefaultItem (USER32.@)
4394 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4400 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4402 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4404 /* find default item */
4408 if (! item) return -1;
4410 while ( !( item->fState & MFS_DEFAULT ) )
4413 if (i >= menu->nItems ) return -1;
4416 /* default: don't return disabled items */
4417 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4419 /* search rekursiv when needed */
4420 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4423 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4424 if ( -1 != ret ) return ret;
4426 /* when item not found in submenu, return the popup item */
4428 return ( bypos ) ? i : item->wID;
4433 /**********************************************************************
4434 * InsertMenuItemA (USER32.@)
4436 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4437 const MENUITEMINFOA *lpmii)
4439 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4440 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4444 /**********************************************************************
4445 * InsertMenuItemW (USER32.@)
4447 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4448 const MENUITEMINFOW *lpmii)
4450 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4451 return SetMenuItemInfo_common(item, lpmii, TRUE);
4454 /**********************************************************************
4455 * CheckMenuRadioItem (USER32.@)
4458 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4459 UINT first, UINT last, UINT check,
4462 MENUITEM *mifirst, *milast, *micheck;
4463 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4465 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4467 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4468 milast = MENU_FindItem (&mlast, &last, bypos);
4469 micheck = MENU_FindItem (&mcheck, &check, bypos);
4471 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4472 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4473 micheck > milast || micheck < mifirst)
4476 while (mifirst <= milast)
4478 if (mifirst == micheck)
4480 mifirst->fType |= MFT_RADIOCHECK;
4481 mifirst->fState |= MFS_CHECKED;
4483 mifirst->fType &= ~MFT_RADIOCHECK;
4484 mifirst->fState &= ~MFS_CHECKED;
4493 /**********************************************************************
4494 * GetMenuItemRect (USER32.@)
4496 * ATTENTION: Here, the returned values in rect are the screen
4497 * coordinates of the item just like if the menu was
4498 * always on the upper left side of the application.
4501 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4504 POPUPMENU *itemMenu;
4508 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4510 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4511 referenceHwnd = hwnd;
4515 itemMenu = MENU_GetMenu(hMenu);
4516 if (itemMenu == NULL)
4519 if(itemMenu->hWnd == 0)
4521 referenceHwnd = itemMenu->hWnd;
4524 if ((rect == NULL) || (item == NULL))
4529 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4535 /**********************************************************************
4536 * SetMenuInfo (USER32.@)
4539 * MIM_APPLYTOSUBMENUS
4540 * actually use the items to draw the menu
4542 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4546 TRACE("(%p %p)\n", hMenu, lpmi);
4548 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4551 if (lpmi->fMask & MIM_BACKGROUND)
4552 menu->hbrBack = lpmi->hbrBack;
4554 if (lpmi->fMask & MIM_HELPID)
4555 menu->dwContextHelpID = lpmi->dwContextHelpID;
4557 if (lpmi->fMask & MIM_MAXHEIGHT)
4558 menu->cyMax = lpmi->cyMax;
4560 if (lpmi->fMask & MIM_MENUDATA)
4561 menu->dwMenuData = lpmi->dwMenuData;
4563 if (lpmi->fMask & MIM_STYLE)
4565 menu->dwStyle = lpmi->dwStyle;
4566 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4567 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4568 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4569 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4570 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4578 /**********************************************************************
4579 * GetMenuInfo (USER32.@)
4585 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4588 TRACE("(%p %p)\n", hMenu, lpmi);
4590 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4593 if (lpmi->fMask & MIM_BACKGROUND)
4594 lpmi->hbrBack = menu->hbrBack;
4596 if (lpmi->fMask & MIM_HELPID)
4597 lpmi->dwContextHelpID = menu->dwContextHelpID;
4599 if (lpmi->fMask & MIM_MAXHEIGHT)
4600 lpmi->cyMax = menu->cyMax;
4602 if (lpmi->fMask & MIM_MENUDATA)
4603 lpmi->dwMenuData = menu->dwMenuData;
4605 if (lpmi->fMask & MIM_STYLE)
4606 lpmi->dwStyle = menu->dwStyle;
4614 /**********************************************************************
4615 * SetMenuContextHelpId (USER32.@)
4617 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4621 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4623 if ((menu = MENU_GetMenu(hMenu)))
4625 menu->dwContextHelpID = dwContextHelpID;
4632 /**********************************************************************
4633 * GetMenuContextHelpId (USER32.@)
4635 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4639 TRACE("(%p)\n", hMenu);
4641 if ((menu = MENU_GetMenu(hMenu)))
4643 return menu->dwContextHelpID;
4648 /**********************************************************************
4649 * MenuItemFromPoint (USER32.@)
4651 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4653 POPUPMENU *menu = MENU_GetMenu(hMenu);
4656 /*FIXME: Do we have to handle hWnd here? */
4657 if (!menu) return -1;
4658 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4663 /**********************************************************************
4664 * translate_accelerator
4666 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4667 BYTE fVirt, WORD key, WORD cmd )
4672 if (wParam != key) return FALSE;
4674 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4675 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4676 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4678 if (message == WM_CHAR || message == WM_SYSCHAR)
4680 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4682 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4688 if(fVirt & FVIRTKEY)
4690 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4691 wParam, 0xff & HIWORD(lParam));
4693 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4694 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4698 if (!(lParam & 0x01000000)) /* no special_key */
4700 if ((fVirt & FALT) && (lParam & 0x20000000))
4701 { /* ^^ ALT pressed */
4702 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4711 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4715 HMENU hMenu, hSubMenu, hSysMenu;
4716 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4718 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4719 hSysMenu = get_win_sys_menu( hWnd );
4721 /* find menu item and ask application to initialize it */
4722 /* 1. in the system menu */
4723 hSubMenu = hSysMenu;
4725 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4729 if (!IsWindowEnabled(hWnd))
4733 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4734 if(hSubMenu != hSysMenu)
4736 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4737 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4738 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4740 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4743 else /* 2. in the window's menu */
4747 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4751 if (!IsWindowEnabled(hWnd))
4755 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4756 if(hSubMenu != hMenu)
4758 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4759 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4760 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4762 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4769 if (uSysStat != (UINT)-1)
4771 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4778 if (uStat != (UINT)-1)
4784 if (uStat & (MF_DISABLED|MF_GRAYED))
4796 if( mesg==WM_COMMAND )
4798 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4799 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4801 else if( mesg==WM_SYSCOMMAND )
4803 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4804 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4808 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4809 * #0: unknown (please report!)
4810 * #1: for WM_KEYUP,WM_SYSKEYUP
4811 * #2: mouse is captured
4812 * #3: window is disabled
4813 * #4: it's a disabled system menu option
4814 * #5: it's a menu option, but window is iconic
4815 * #6: it's a menu option, but disabled
4817 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4819 ERR_(accel)(" unknown reason - please report!\n");
4824 /**********************************************************************
4825 * TranslateAccelerator (USER32.@)
4826 * TranslateAcceleratorA (USER32.@)
4828 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4831 LPACCEL16 lpAccelTbl;
4835 if (!hWnd || !msg) return 0;
4837 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4839 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4843 wParam = msg->wParam;
4845 switch (msg->message)
4854 char ch = LOWORD(wParam);
4856 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4857 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4865 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4866 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4870 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4871 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4873 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4878 /**********************************************************************
4879 * TranslateAcceleratorW (USER32.@)
4881 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4884 LPACCEL16 lpAccelTbl;
4887 if (!hWnd || !msg) return 0;
4889 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4891 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4895 switch (msg->message)
4907 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4908 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4912 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4913 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4915 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);