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 static SIZE menucharsize;
166 static UINT ODitemheight; /* default owner drawn item height */
168 /* Use global popup window because there's no way 2 menus can
169 * be tracked at the same time. */
170 static HWND top_popup;
172 /* Flag set by EndMenu() to force an exit from menu tracking */
173 static BOOL fEndMenu = FALSE;
175 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
177 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
179 /*********************************************************************
180 * menu class descriptor
182 const struct builtin_class_descr MENU_builtin_class =
184 POPUPMENU_CLASS_ATOMA, /* name */
185 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
186 NULL, /* procA (winproc is Unicode only) */
187 PopupMenuWndProc, /* procW */
188 sizeof(HMENU), /* extra */
189 IDC_ARROW, /* cursor */
190 (HBRUSH)(COLOR_MENU+1) /* brush */
194 /***********************************************************************
195 * debug_print_menuitem
197 * Print a menuitem in readable form.
200 #define debug_print_menuitem(pre, mp, post) \
201 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
203 #define MENUOUT(text) \
204 DPRINTF("%s%s", (count++ ? "," : ""), (text))
206 #define MENUFLAG(bit,text) \
208 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
211 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
214 TRACE("%s ", prefix);
216 UINT flags = mp->fType;
217 int type = MENU_ITEM_TYPE(flags);
218 DPRINTF( "{ ID=0x%x", mp->wID);
219 if (flags & MF_POPUP)
220 DPRINTF( ", Sub=%p", mp->hSubMenu);
224 if (type == MFT_STRING)
226 else if (type == MFT_SEPARATOR)
228 else if (type == MFT_OWNERDRAW)
230 else if (type == MFT_BITMAP)
236 MENUFLAG(MF_POPUP, "pop");
237 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
238 MENUFLAG(MFT_MENUBREAK, "brk");
239 MENUFLAG(MFT_RADIOCHECK, "radio");
240 MENUFLAG(MFT_RIGHTORDER, "rorder");
241 MENUFLAG(MF_SYSMENU, "sys");
242 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 DPRINTF( "+0x%x", flags);
250 DPRINTF( ", State=");
251 MENUFLAG(MFS_GRAYED, "grey");
252 MENUFLAG(MFS_DEFAULT, "default");
253 MENUFLAG(MFS_DISABLED, "dis");
254 MENUFLAG(MFS_CHECKED, "check");
255 MENUFLAG(MFS_HILITE, "hi");
256 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
257 MENUFLAG(MF_MOUSESELECT, "mouse");
259 DPRINTF( "+0x%x", flags);
262 DPRINTF( ", Chk=%p", mp->hCheckBit);
264 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
266 if (type == MFT_STRING) {
268 DPRINTF( ", Text=%s", debugstr_w(mp->text));
270 DPRINTF( ", Text=Null");
271 } else if (mp->text == NULL)
274 DPRINTF( ", Text=%p", mp->text);
276 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
282 DPRINTF(" %s\n", postfix);
289 /***********************************************************************
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
297 if (!menu || menu->wMagic != MENU_MAGIC)
299 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
305 /***********************************************************************
308 * Get the system menu of a window
310 static HMENU get_win_sys_menu( HWND hwnd )
313 WND *win = WIN_GetPtr( hwnd );
314 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
317 WIN_ReleasePtr( win );
322 /***********************************************************************
325 static HFONT get_menu_font( BOOL bold )
327 static HFONT hMenuFont, hMenuFontBold;
329 HFONT ret = bold ? hMenuFontBold : hMenuFont;
333 NONCLIENTMETRICSW ncm;
336 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
337 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
341 ncm.lfMenuFont.lfWeight += 300;
342 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
344 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
345 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 /* another thread beat us to it */
357 /***********************************************************************
360 static HBITMAP get_arrow_bitmap(void)
362 static HBITMAP arrow_bitmap;
364 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 /***********************************************************************
371 * Return the default system menu.
373 static HMENU MENU_CopySysPopup(void)
375 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
376 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
379 POPUPMENU* menu = MENU_GetMenu(hMenu);
380 menu->wFlags |= MF_SYSMENU | MF_POPUP;
381 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
384 ERR("Unable to load default system menu\n" );
386 TRACE("returning %p.\n", hMenu );
392 /**********************************************************************
395 * Create a copy of the system menu. System menu in Windows is
396 * a special menu bar with the single entry - system menu popup.
397 * This popup is presented to the outside world as a "system menu".
398 * However, the real system menu handle is sometimes seen in the
399 * WM_MENUSELECT parameters (and Word 6 likes it this way).
401 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
405 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
406 if ((hMenu = CreateMenu()))
408 POPUPMENU *menu = MENU_GetMenu(hMenu);
409 menu->wFlags = MF_SYSMENU;
410 menu->hWnd = WIN_GetFullHandle( hWnd );
411 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
414 hPopupMenu = MENU_CopySysPopup();
418 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
419 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
421 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
422 (UINT_PTR)hPopupMenu, NULL );
424 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
425 menu->items[0].fState = 0;
426 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
428 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
431 DestroyMenu( hMenu );
433 ERR("failed to load system menu!\n");
438 /***********************************************************************
439 * MENU_InitSysMenuPopup
441 * Grey the appropriate items in System menu.
443 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
447 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
448 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
449 gray = ((style & WS_MAXIMIZE) != 0);
450 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
451 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
452 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
453 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
454 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
455 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
456 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
457 gray = (clsStyle & CS_NOCLOSE) != 0;
459 /* The menu item must keep its state if it's disabled */
461 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
465 /******************************************************************************
467 * UINT MENU_GetStartOfNextColumn(
470 *****************************************************************************/
472 static UINT MENU_GetStartOfNextColumn(
475 POPUPMENU *menu = MENU_GetMenu(hMenu);
479 return NO_SELECTED_ITEM;
481 i = menu->FocusedItem + 1;
482 if( i == NO_SELECTED_ITEM )
485 for( ; i < menu->nItems; ++i ) {
486 if (menu->items[i].fType & MF_MENUBARBREAK)
490 return NO_SELECTED_ITEM;
494 /******************************************************************************
496 * UINT MENU_GetStartOfPrevColumn(
499 *****************************************************************************/
501 static UINT MENU_GetStartOfPrevColumn(
504 POPUPMENU *menu = MENU_GetMenu(hMenu);
508 return NO_SELECTED_ITEM;
510 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
511 return NO_SELECTED_ITEM;
513 /* Find the start of the column */
515 for(i = menu->FocusedItem; i != 0 &&
516 !(menu->items[i].fType & MF_MENUBARBREAK);
520 return NO_SELECTED_ITEM;
522 for(--i; i != 0; --i) {
523 if (menu->items[i].fType & MF_MENUBARBREAK)
527 TRACE("ret %d.\n", i );
534 /***********************************************************************
537 * Find a menu item. Return a pointer on the item, and modifies *hmenu
538 * in case the item was in a sub-menu.
540 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
545 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
546 if (wFlags & MF_BYPOSITION)
548 if (*nPos >= menu->nItems) return NULL;
549 return &menu->items[*nPos];
553 MENUITEM *item = menu->items;
554 for (i = 0; i < menu->nItems; i++, item++)
556 if (item->wID == *nPos)
561 else if (item->fType & MF_POPUP)
563 HMENU hsubmenu = item->hSubMenu;
564 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
576 /***********************************************************************
579 * Find a Sub menu. Return the position of the submenu, and modifies
580 * *hmenu in case it is found in another sub-menu.
581 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
583 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
588 if (((*hmenu)==(HMENU)0xffff) ||
589 (!(menu = MENU_GetMenu(*hmenu))))
590 return NO_SELECTED_ITEM;
592 for (i = 0; i < menu->nItems; i++, item++) {
593 if(!(item->fType & MF_POPUP)) continue;
594 if (item->hSubMenu == hSubTarget) {
598 HMENU hsubmenu = item->hSubMenu;
599 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
600 if (pos != NO_SELECTED_ITEM) {
606 return NO_SELECTED_ITEM;
609 /***********************************************************************
612 static void MENU_FreeItemData( MENUITEM* item )
615 if (IS_STRING_ITEM(item->fType) && item->text)
616 HeapFree( GetProcessHeap(), 0, item->text );
619 /***********************************************************************
620 * MENU_FindItemByCoords
622 * Find the item at the specified coordinates (screen coords). Does
623 * not work for child windows and therefore should not be called for
624 * an arbitrary system menu.
626 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
627 POINT pt, UINT *pos )
633 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
634 pt.x -= wrect.left;pt.y -= wrect.top;
636 for (i = 0; i < menu->nItems; i++, item++)
638 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
639 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
649 /***********************************************************************
652 * Find the menu item selected by a key press.
653 * Return item id, -1 if none, -2 if we should close the menu.
655 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
656 WCHAR key, BOOL forceMenuChar )
658 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
660 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
664 POPUPMENU *menu = MENU_GetMenu( hmenu );
665 MENUITEM *item = menu->items;
672 for (i = 0; i < menu->nItems; i++, item++)
674 if (IS_STRING_ITEM(item->fType) && item->text)
676 WCHAR *p = item->text - 2;
679 p = strchrW (p + 2, '&');
681 while (p != NULL && p [1] == '&');
682 if (p && (toupperW(p[1]) == toupperW(key))) return i;
686 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
687 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
688 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
689 if (HIWORD(menuchar) == 1) return (UINT)(-2);
695 /***********************************************************************
696 * MENU_GetBitmapItemSize
698 * Get the size of a bitmap item.
700 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
703 HBITMAP bmp = (HBITMAP)id;
705 size->cx = size->cy = 0;
707 /* check if there is a magic menu item associated with this item */
708 if (id && IS_MAGIC_ITEM( id ))
712 case (INT_PTR)HBMMENU_SYSTEM:
719 case (INT_PTR)HBMMENU_MBAR_RESTORE:
720 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
721 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
722 case (INT_PTR)HBMMENU_MBAR_CLOSE:
723 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
724 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
727 case (INT_PTR)HBMMENU_CALLBACK:
728 case (INT_PTR)HBMMENU_POPUP_CLOSE:
729 case (INT_PTR)HBMMENU_POPUP_RESTORE:
730 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
731 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
733 FIXME("Magic 0x%08x not implemented\n", id);
737 if (GetObjectW(bmp, sizeof(bm), &bm ))
739 size->cx = bm.bmWidth;
740 size->cy = bm.bmHeight;
744 /***********************************************************************
745 * MENU_DrawBitmapItem
747 * Draw a bitmap item.
748 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
750 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
755 HBITMAP bmp = (HBITMAP)lpitem->text;
756 int w = rect->right - rect->left;
757 int h = rect->bottom - rect->top;
760 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
762 /* Check if there is a magic menu item associated with this item */
763 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
768 switch(LOWORD(hbmToDraw))
770 case (INT_PTR)HBMMENU_SYSTEM:
771 if (lpitem->dwItemData)
773 bmp = (HBITMAP)lpitem->dwItemData;
774 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
778 static HBITMAP hBmpSysMenu;
780 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
782 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
783 /* only use right half of the bitmap */
784 bmp_xoffset = bm.bmWidth / 2;
785 bm.bmWidth -= bmp_xoffset;
788 case (INT_PTR)HBMMENU_MBAR_RESTORE:
789 flags = DFCS_CAPTIONRESTORE;
791 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
792 flags = DFCS_CAPTIONMIN;
794 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
795 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
797 case (INT_PTR)HBMMENU_MBAR_CLOSE:
798 flags = DFCS_CAPTIONCLOSE;
800 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
801 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
803 case (INT_PTR)HBMMENU_CALLBACK:
804 case (INT_PTR)HBMMENU_POPUP_CLOSE:
805 case (INT_PTR)HBMMENU_POPUP_RESTORE:
806 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
807 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
809 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
813 InflateRect( &r, -1, -1 );
814 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
815 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
819 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
822 hdcMem = CreateCompatibleDC( hdc );
823 SelectObject( hdcMem, bmp );
825 /* handle fontsize > bitmap_height */
826 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
828 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
829 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
830 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
831 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
836 /***********************************************************************
839 * Calculate the size of the menu item and store it in lpitem->rect.
841 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
842 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
845 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
846 UINT arrow_bitmap_width;
849 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
850 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
851 (menuBar ? " (MenuBar)" : ""));
853 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
854 arrow_bitmap_width = bm.bmWidth;
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 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
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, get_menu_font(FALSE));
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 );
1300 UINT arrow_bitmap_width, arrow_bitmap_height;
1303 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1304 arrow_bitmap_width = bmp.bmWidth;
1305 arrow_bitmap_height = bmp.bmHeight;
1307 if (!(lpitem->fType & MF_OWNERDRAW))
1311 /* New style MIIM_BITMAP */
1312 if (lpitem->hbmpItem)
1314 POPUPMENU *menu = MENU_GetMenu(hmenu);
1315 if (menu->dwStyle & MNS_CHECKORBMP)
1316 rc.left += menu->maxBmpSize.cx - check_bitmap_width;
1318 rc.left += menu->maxBmpSize.cx;
1320 /* Draw the check mark
1323 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1325 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1326 if (bm) /* we have a custom bitmap */
1328 HDC hdcMem = CreateCompatibleDC( hdc );
1329 SelectObject( hdcMem, bm );
1330 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1331 check_bitmap_width, check_bitmap_height,
1332 hdcMem, 0, 0, SRCCOPY );
1335 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1338 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1339 HDC hdcMem = CreateCompatibleDC( hdc );
1340 SelectObject( hdcMem, bm );
1341 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1342 DrawFrameControl( hdcMem, &r, DFC_MENU,
1343 (lpitem->fType & MFT_RADIOCHECK) ?
1344 DFCS_MENUBULLET : DFCS_MENUCHECK );
1345 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1346 hdcMem, 0, 0, SRCCOPY );
1350 /* New style MIIM_BITMAP */
1351 if (lpitem->hbmpItem)
1353 HBITMAP hbm = lpitem->hbmpItem;
1355 if (hbm == HBMMENU_CALLBACK)
1357 DRAWITEMSTRUCT drawItem;
1359 drawItem.CtlType = ODT_MENU;
1361 drawItem.itemID = lpitem->wID;
1362 drawItem.itemAction = odaction;
1363 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1364 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1365 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1366 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1367 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1368 drawItem.hwndItem = (HWND)hmenu;
1370 drawItem.rcItem = lpitem->rect;
1371 drawItem.itemData = lpitem->dwItemData;
1372 /* some applications make this assumption on the DC's origin */
1373 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1374 OffsetRect( &drawItem.rcItem, - lpitem->rect.left, - lpitem->rect.top);
1375 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1376 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1379 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1384 /* Draw the popup-menu arrow */
1385 if (lpitem->fType & MF_POPUP)
1387 HDC hdcMem = CreateCompatibleDC( hdc );
1388 HBITMAP hOrigBitmap;
1390 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1391 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1392 (y - arrow_bitmap_height) / 2,
1393 arrow_bitmap_width, arrow_bitmap_height,
1394 hdcMem, 0, 0, SRCCOPY );
1395 SelectObject( hdcMem, hOrigBitmap );
1399 rect.left += check_bitmap_width;
1400 rect.right -= arrow_bitmap_width;
1403 /* Done for owner-drawn */
1404 if (lpitem->fType & MF_OWNERDRAW)
1407 /* Draw the item text or bitmap */
1408 if (IS_BITMAP_ITEM(lpitem->fType))
1410 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1413 /* No bitmap - process text if present */
1414 else if (IS_STRING_ITEM(lpitem->fType))
1419 UINT uFormat = (menuBar) ?
1420 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1421 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1423 if ( lpitem->fState & MFS_DEFAULT )
1425 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1430 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1431 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1434 for (i = 0; lpitem->text[i]; i++)
1435 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1438 if(lpitem->fState & MF_GRAYED)
1440 if (!(lpitem->fState & MF_HILITE) )
1442 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1443 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1444 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1445 --rect.left; --rect.top; --rect.right; --rect.bottom;
1447 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1450 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1452 /* paint the shortcut text */
1453 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1455 if (lpitem->text[i] == '\t')
1457 rect.left = lpitem->xTab;
1458 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1462 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1465 if(lpitem->fState & MF_GRAYED)
1467 if (!(lpitem->fState & MF_HILITE) )
1469 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1470 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1471 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1472 --rect.left; --rect.top; --rect.right; --rect.bottom;
1474 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1476 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1480 SelectObject (hdc, hfontOld);
1485 /***********************************************************************
1486 * MENU_DrawPopupMenu
1488 * Paint a popup menu.
1490 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1492 HBRUSH hPrevBrush = 0;
1495 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1497 GetClientRect( hwnd, &rect );
1499 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1500 && (SelectObject( hdc, get_menu_font(FALSE))))
1504 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1506 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1510 BOOL flat_menu = FALSE;
1512 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1514 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1516 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1518 /* draw menu items */
1520 menu = MENU_GetMenu( hmenu );
1521 if (menu && menu->nItems)
1526 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1527 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1528 menu->Height, FALSE, ODA_DRAWENTIRE );
1533 SelectObject( hdc, hPrevBrush );
1538 /***********************************************************************
1541 * Paint a menu bar. Returns the height of the menu bar.
1542 * called from [windows/nonclient.c]
1544 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1549 HMENU hMenu = GetMenu(hwnd);
1551 lppop = MENU_GetMenu( hMenu );
1552 if (lppop == NULL || lprect == NULL)
1554 return GetSystemMetrics(SM_CYMENU);
1559 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1561 if (lppop->Height == 0)
1562 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1564 lprect->bottom = lprect->top + lppop->Height;
1566 if (hfontOld) SelectObject( hDC, hfontOld);
1567 return lppop->Height;
1570 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1574 /***********************************************************************
1577 * Display a popup menu.
1579 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1580 INT x, INT y, INT xanchor, INT yanchor )
1585 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1586 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1588 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1589 if (menu->FocusedItem != NO_SELECTED_ITEM)
1591 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1592 menu->FocusedItem = NO_SELECTED_ITEM;
1595 /* store the owner for DrawItem */
1596 menu->hwndOwner = hwndOwner;
1598 MENU_PopupMenuCalcSize( menu, hwndOwner );
1600 /* adjust popup menu pos so that it fits within the desktop */
1602 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1603 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1605 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1608 x -= width - xanchor;
1609 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1610 x = GetSystemMetrics(SM_CXSCREEN) - width;
1614 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1617 y -= height + yanchor;
1618 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1619 y = GetSystemMetrics(SM_CYSCREEN) - height;
1623 /* NOTE: In Windows, top menu popup is not owned. */
1624 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1625 WS_POPUP, x, y, width, height,
1626 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1628 if( !menu->hWnd ) return FALSE;
1629 if (!top_popup) top_popup = menu->hWnd;
1631 /* Display the window */
1633 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1634 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1635 UpdateWindow( menu->hWnd );
1640 /***********************************************************************
1643 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1644 BOOL sendMenuSelect, HMENU topmenu )
1649 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1651 lppop = MENU_GetMenu( hmenu );
1652 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1654 if (lppop->FocusedItem == wIndex) return;
1655 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1656 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1657 if (!top_popup) top_popup = lppop->hWnd;
1659 SelectObject( hdc, get_menu_font(FALSE));
1661 /* Clear previous highlighted item */
1662 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1664 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1665 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1666 lppop->Height, !(lppop->wFlags & MF_POPUP),
1670 /* Highlight new item (if any) */
1671 lppop->FocusedItem = wIndex;
1672 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1674 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1675 lppop->items[wIndex].fState |= MF_HILITE;
1676 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1677 &lppop->items[wIndex], lppop->Height,
1678 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1682 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1683 SendMessageW( hwndOwner, WM_MENUSELECT,
1684 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1685 ip->fType | ip->fState |
1686 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1689 else if (sendMenuSelect) {
1692 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1693 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1694 MENUITEM *ip = &ptm->items[pos];
1695 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1696 ip->fType | ip->fState |
1697 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1701 ReleaseDC( lppop->hWnd, hdc );
1705 /***********************************************************************
1706 * MENU_MoveSelection
1708 * Moves currently selected item according to the offset parameter.
1709 * If there is no selection then it should select the last item if
1710 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1712 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1717 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1719 menu = MENU_GetMenu( hmenu );
1720 if ((!menu) || (!menu->items)) return;
1722 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1724 if( menu->nItems == 1 ) return; else
1725 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1727 if (!(menu->items[i].fType & MF_SEPARATOR))
1729 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1734 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1735 i >= 0 && i < menu->nItems ; i += offset)
1736 if (!(menu->items[i].fType & MF_SEPARATOR))
1738 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1744 /**********************************************************************
1747 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1750 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1753 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1755 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1756 TRACE("flags=%x str=%p\n", flags, str);
1758 if (IS_STRING_ITEM(flags))
1762 flags |= MF_SEPARATOR;
1768 /* Item beginning with a backspace is a help item */
1774 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1776 strcpyW( text, str );
1780 else if (IS_BITMAP_ITEM(flags))
1781 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1782 else item->text = NULL;
1784 if (flags & MF_OWNERDRAW)
1785 item->dwItemData = (DWORD)str;
1787 item->dwItemData = 0;
1789 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1790 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1792 if (flags & MF_POPUP)
1794 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1795 if (menu) menu->wFlags |= MF_POPUP;
1807 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1809 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1810 flags |= MF_POPUP; /* keep popup */
1812 item->fType = flags & TYPE_MASK;
1813 item->fState = (flags & STATE_MASK) &
1814 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1817 /* Don't call SetRectEmpty here! */
1820 HeapFree( GetProcessHeap(), 0, prevText );
1822 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1827 /**********************************************************************
1830 * Insert (allocate) a new item into a menu.
1832 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1837 if (!(menu = MENU_GetMenu(hMenu)))
1840 /* Find where to insert new item */
1842 if (flags & MF_BYPOSITION) {
1843 if (pos > menu->nItems)
1846 if (!MENU_FindItem( &hMenu, &pos, flags ))
1849 if (!(menu = MENU_GetMenu( hMenu )))
1854 /* Create new items array */
1856 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1859 WARN("allocation failed\n" );
1862 if (menu->nItems > 0)
1864 /* Copy the old array into the new one */
1865 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1866 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1867 (menu->nItems-pos)*sizeof(MENUITEM) );
1868 HeapFree( GetProcessHeap(), 0, menu->items );
1870 menu->items = newItems;
1872 memset( &newItems[pos], 0, sizeof(*newItems) );
1873 menu->Height = 0; /* force size recalculate */
1874 return &newItems[pos];
1878 /**********************************************************************
1879 * MENU_ParseResource
1881 * Parse a standard menu resource and add items to the menu.
1882 * Return a pointer to the end of the resource.
1884 * NOTE: flags is equivalent to the mtOption field
1886 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1893 flags = GET_WORD(res);
1894 res += sizeof(WORD);
1895 if (!(flags & MF_POPUP))
1898 res += sizeof(WORD);
1901 if (!unicode) res += strlen(str) + 1;
1902 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1903 if (flags & MF_POPUP)
1905 HMENU hSubMenu = CreatePopupMenu();
1906 if (!hSubMenu) return NULL;
1907 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1909 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1910 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1912 else /* Not a popup */
1914 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1915 else AppendMenuW( hMenu, flags, id,
1916 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1918 } while (!(flags & MF_END));
1923 /**********************************************************************
1924 * MENUEX_ParseResource
1926 * Parse an extended menu resource and add items to the menu.
1927 * Return a pointer to the end of the resource.
1929 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1935 mii.cbSize = sizeof(mii);
1936 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1937 mii.fType = GET_DWORD(res);
1938 res += sizeof(DWORD);
1939 mii.fState = GET_DWORD(res);
1940 res += sizeof(DWORD);
1941 mii.wID = GET_DWORD(res);
1942 res += sizeof(DWORD);
1943 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1944 res += sizeof(WORD);
1945 /* Align the text on a word boundary. */
1946 res += (~((int)res - 1)) & 1;
1947 mii.dwTypeData = (LPWSTR) res;
1948 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1949 /* Align the following fields on a dword boundary. */
1950 res += (~((int)res - 1)) & 3;
1952 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1953 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1955 if (resinfo & 1) { /* Pop-up? */
1956 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1957 res += sizeof(DWORD);
1958 mii.hSubMenu = CreatePopupMenu();
1961 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1962 DestroyMenu(mii.hSubMenu);
1965 mii.fMask |= MIIM_SUBMENU;
1966 mii.fType |= MF_POPUP;
1968 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1970 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1971 mii.wID, mii.fType);
1972 mii.fType |= MF_SEPARATOR;
1974 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1975 } while (!(resinfo & MF_END));
1980 /***********************************************************************
1983 * Return the handle of the selected sub-popup menu (if any).
1985 static HMENU MENU_GetSubPopup( HMENU hmenu )
1990 menu = MENU_GetMenu( hmenu );
1992 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1994 item = &menu->items[menu->FocusedItem];
1995 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1996 return item->hSubMenu;
2001 /***********************************************************************
2002 * MENU_HideSubPopups
2004 * Hide the sub-popup menus of this menu.
2006 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2007 BOOL sendMenuSelect )
2009 POPUPMENU *menu = MENU_GetMenu( hmenu );
2011 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2013 if (menu && top_popup)
2019 if (menu->FocusedItem != NO_SELECTED_ITEM)
2021 item = &menu->items[menu->FocusedItem];
2022 if (!(item->fType & MF_POPUP) ||
2023 !(item->fState & MF_MOUSESELECT)) return;
2024 item->fState &= ~MF_MOUSESELECT;
2025 hsubmenu = item->hSubMenu;
2028 submenu = MENU_GetMenu( hsubmenu );
2029 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2030 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2031 DestroyWindow( submenu->hWnd );
2037 /***********************************************************************
2040 * Display the sub-menu of the selected item of this menu.
2041 * Return the handle of the submenu, or hmenu if no submenu to display.
2043 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2044 BOOL selectFirst, UINT wFlags )
2051 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2053 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2055 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2057 item = &menu->items[menu->FocusedItem];
2058 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2061 /* message must be sent before using item,
2062 because nearly everything may be changed by the application ! */
2064 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2065 if (!(wFlags & TPM_NONOTIFY))
2066 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2067 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2069 item = &menu->items[menu->FocusedItem];
2072 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2073 if (!(item->fState & MF_HILITE))
2075 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2076 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2078 SelectObject( hdc, get_menu_font(FALSE));
2080 item->fState |= MF_HILITE;
2081 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2082 ReleaseDC( menu->hWnd, hdc );
2084 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2087 item->fState |= MF_MOUSESELECT;
2089 if (IS_SYSTEM_MENU(menu))
2091 MENU_InitSysMenuPopup(item->hSubMenu,
2092 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2093 GetClassLongW( menu->hWnd, GCL_STYLE));
2095 NC_GetSysPopupPos( menu->hWnd, &rect );
2096 rect.top = rect.bottom;
2097 rect.right = GetSystemMetrics(SM_CXSIZE);
2098 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2102 GetWindowRect( menu->hWnd, &rect );
2103 if (menu->wFlags & MF_POPUP)
2105 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2106 rect.top += item->rect.top;
2107 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2108 rect.bottom = item->rect.top - item->rect.bottom;
2112 rect.left += item->rect.left;
2113 rect.top += item->rect.bottom;
2114 rect.right = item->rect.right - item->rect.left;
2115 rect.bottom = item->rect.bottom - item->rect.top;
2119 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2120 rect.left, rect.top, rect.right, rect.bottom );
2122 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2123 return item->hSubMenu;
2128 /**********************************************************************
2131 HWND MENU_IsMenuActive(void)
2136 /***********************************************************************
2139 * Walks menu chain trying to find a menu pt maps to.
2141 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2143 POPUPMENU *menu = MENU_GetMenu( hMenu );
2144 UINT item = menu->FocusedItem;
2147 /* try subpopup first (if any) */
2148 ret = (item != NO_SELECTED_ITEM &&
2149 (menu->items[item].fType & MF_POPUP) &&
2150 (menu->items[item].fState & MF_MOUSESELECT))
2151 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2153 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2155 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2156 if( menu->wFlags & MF_POPUP )
2158 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2160 else if (ht == HTSYSMENU)
2161 ret = get_win_sys_menu( menu->hWnd );
2162 else if (ht == HTMENU)
2163 ret = GetMenu( menu->hWnd );
2168 /***********************************************************************
2169 * MENU_ExecFocusedItem
2171 * Execute a menu item (for instance when user pressed Enter).
2172 * Return the wID of the executed item. Otherwise, -1 indicating
2173 * that no menu item was executed;
2174 * Have to receive the flags for the TrackPopupMenu options to avoid
2175 * sending unwanted message.
2178 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2181 POPUPMENU *menu = MENU_GetMenu( hMenu );
2183 TRACE("%p hmenu=%p\n", pmt, hMenu);
2185 if (!menu || !menu->nItems ||
2186 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2188 item = &menu->items[menu->FocusedItem];
2190 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2192 if (!(item->fType & MF_POPUP))
2194 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2196 /* If TPM_RETURNCMD is set you return the id, but
2197 do not send a message to the owner */
2198 if(!(wFlags & TPM_RETURNCMD))
2200 if( menu->wFlags & MF_SYSMENU )
2201 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2202 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2204 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2210 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2215 /***********************************************************************
2216 * MENU_SwitchTracking
2218 * Helper function for menu navigation routines.
2220 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2222 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2223 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2225 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2227 if( pmt->hTopMenu != hPtMenu &&
2228 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2230 /* both are top level menus (system and menu-bar) */
2231 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2232 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2233 pmt->hTopMenu = hPtMenu;
2235 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2236 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2240 /***********************************************************************
2243 * Return TRUE if we can go on with menu tracking.
2245 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2247 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2252 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2255 if( IS_SYSTEM_MENU(ptmenu) )
2256 item = ptmenu->items;
2258 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2262 if( ptmenu->FocusedItem != id )
2263 MENU_SwitchTracking( pmt, hPtMenu, id );
2265 /* If the popup menu is not already "popped" */
2266 if(!(item->fState & MF_MOUSESELECT ))
2268 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2273 /* Else the click was on the menu bar, finish the tracking */
2278 /***********************************************************************
2281 * Return the value of MENU_ExecFocusedItem if
2282 * the selected item was not a popup. Else open the popup.
2283 * A -1 return value indicates that we go on with menu tracking.
2286 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2288 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2293 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2296 if( IS_SYSTEM_MENU(ptmenu) )
2297 item = ptmenu->items;
2299 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2301 if( item && (ptmenu->FocusedItem == id ))
2303 if( !(item->fType & MF_POPUP) )
2304 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2306 /* If we are dealing with the top-level menu */
2307 /* and this is a click on an already "popped" item: */
2308 /* Stop the menu tracking and close the opened submenus */
2309 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2312 ptmenu->bTimeToHide = TRUE;
2318 /***********************************************************************
2321 * Return TRUE if we can go on with menu tracking.
2323 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2325 UINT id = NO_SELECTED_ITEM;
2326 POPUPMENU *ptmenu = NULL;
2330 ptmenu = MENU_GetMenu( hPtMenu );
2331 if( IS_SYSTEM_MENU(ptmenu) )
2334 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2337 if( id == NO_SELECTED_ITEM )
2339 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2340 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2343 else if( ptmenu->FocusedItem != id )
2345 MENU_SwitchTracking( pmt, hPtMenu, id );
2346 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2352 /***********************************************************************
2355 static void MENU_SetCapture( HWND hwnd )
2359 SERVER_START_REQ( set_capture_window )
2362 req->flags = CAPTURE_MENU;
2363 if (!wine_server_call_err( req ))
2365 previous = reply->previous;
2366 hwnd = reply->full_handle;
2371 if (previous && previous != hwnd)
2372 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2376 /***********************************************************************
2379 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2381 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2383 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2385 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2386 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2388 MDINEXTMENU next_menu;
2393 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2394 next_menu.hmenuNext = 0;
2395 next_menu.hwndNext = 0;
2396 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2398 TRACE("%p [%p] -> %p [%p]\n",
2399 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2401 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2403 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2404 hNewWnd = pmt->hOwnerWnd;
2405 if( IS_SYSTEM_MENU(menu) )
2407 /* switch to the menu bar */
2409 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2413 menu = MENU_GetMenu( hNewMenu );
2414 id = menu->nItems - 1;
2417 else if (style & WS_SYSMENU )
2419 /* switch to the system menu */
2420 hNewMenu = get_win_sys_menu( hNewWnd );
2424 else /* application returned a new menu to switch to */
2426 hNewMenu = next_menu.hmenuNext;
2427 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2429 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2431 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2433 if (style & WS_SYSMENU &&
2434 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2436 /* get the real system menu */
2437 hNewMenu = get_win_sys_menu(hNewWnd);
2439 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2441 /* FIXME: Not sure what to do here;
2442 * perhaps try to track hNewMenu as a popup? */
2444 TRACE(" -- got confused.\n");
2451 if( hNewMenu != pmt->hTopMenu )
2453 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2455 if( pmt->hCurrentMenu != pmt->hTopMenu )
2456 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2459 if( hNewWnd != pmt->hOwnerWnd )
2461 pmt->hOwnerWnd = hNewWnd;
2462 MENU_SetCapture( pmt->hOwnerWnd );
2465 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2466 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2473 /***********************************************************************
2476 * The idea is not to show the popup if the next input message is
2477 * going to hide it anyway.
2479 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2483 msg.hwnd = pmt->hOwnerWnd;
2485 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2486 pmt->trackFlags |= TF_SKIPREMOVE;
2491 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2492 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2494 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2495 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2496 if( msg.message == WM_KEYDOWN &&
2497 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2499 pmt->trackFlags |= TF_SUSPENDPOPUP;
2506 /* failures go through this */
2507 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2511 /***********************************************************************
2514 * Handle a VK_ESCAPE key event in a menu.
2516 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2518 BOOL bEndMenu = TRUE;
2520 if (pmt->hCurrentMenu != pmt->hTopMenu)
2522 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2524 if (menu->wFlags & MF_POPUP)
2526 HMENU hmenutmp, hmenuprev;
2528 hmenuprev = hmenutmp = pmt->hTopMenu;
2530 /* close topmost popup */
2531 while (hmenutmp != pmt->hCurrentMenu)
2533 hmenuprev = hmenutmp;
2534 hmenutmp = MENU_GetSubPopup( hmenuprev );
2537 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2538 pmt->hCurrentMenu = hmenuprev;
2546 /***********************************************************************
2549 * Handle a VK_LEFT key event in a menu.
2551 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2554 HMENU hmenutmp, hmenuprev;
2557 hmenuprev = hmenutmp = pmt->hTopMenu;
2558 menu = MENU_GetMenu( hmenutmp );
2560 /* Try to move 1 column left (if possible) */
2561 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2562 NO_SELECTED_ITEM ) {
2564 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2569 /* close topmost popup */
2570 while (hmenutmp != pmt->hCurrentMenu)
2572 hmenuprev = hmenutmp;
2573 hmenutmp = MENU_GetSubPopup( hmenuprev );
2576 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2577 pmt->hCurrentMenu = hmenuprev;
2579 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2581 /* move menu bar selection if no more popups are left */
2583 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2584 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2586 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2588 /* A sublevel menu was displayed - display the next one
2589 * unless there is another displacement coming up */
2591 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2592 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2593 pmt->hTopMenu, TRUE, wFlags);
2599 /***********************************************************************
2602 * Handle a VK_RIGHT key event in a menu.
2604 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2607 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2610 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2612 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2613 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2615 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2617 /* If already displaying a popup, try to display sub-popup */
2619 hmenutmp = pmt->hCurrentMenu;
2620 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2622 /* if subpopup was displayed then we are done */
2623 if (hmenutmp != pmt->hCurrentMenu) return;
2626 /* Check to see if there's another column */
2627 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2628 NO_SELECTED_ITEM ) {
2629 TRACE("Going to %d.\n", nextcol );
2630 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2635 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2637 if( pmt->hCurrentMenu != pmt->hTopMenu )
2639 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2640 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2641 } else hmenutmp = 0;
2643 /* try to move to the next item */
2644 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2645 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2647 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2648 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2649 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2650 pmt->hTopMenu, TRUE, wFlags);
2654 /***********************************************************************
2657 * Menu tracking code.
2659 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2660 HWND hwnd, const RECT *lprect )
2665 INT executedMenuId = -1;
2667 BOOL enterIdleSent = FALSE;
2670 mt.hCurrentMenu = hmenu;
2671 mt.hTopMenu = hmenu;
2672 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2676 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2677 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2678 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2681 if (!(menu = MENU_GetMenu( hmenu )))
2683 WARN("Invalid menu handle %p\n", hmenu);
2684 SetLastError(ERROR_INVALID_MENU_HANDLE);
2688 if (wFlags & TPM_BUTTONDOWN)
2690 /* Get the result in order to start the tracking or not */
2691 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2692 fEndMenu = !fRemove;
2695 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2697 MENU_SetCapture( mt.hOwnerWnd );
2701 menu = MENU_GetMenu( mt.hCurrentMenu );
2702 if (!menu) /* sometimes happens if I do a window manager close */
2705 /* we have to keep the message in the queue until it's
2706 * clear that menu loop is not over yet. */
2710 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2712 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2713 /* remove the message from the queue */
2714 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2720 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2721 enterIdleSent = TRUE;
2722 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2728 /* check if EndMenu() tried to cancel us, by posting this message */
2729 if(msg.message == WM_CANCELMODE)
2731 /* we are now out of the loop */
2734 /* remove the message from the queue */
2735 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2737 /* break out of internal loop, ala ESCAPE */
2741 TranslateMessage( &msg );
2744 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2745 enterIdleSent=FALSE;
2748 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2751 * Use the mouse coordinates in lParam instead of those in the MSG
2752 * struct to properly handle synthetic messages. They are already
2753 * in screen coordinates.
2755 mt.pt.x = (short)LOWORD(msg.lParam);
2756 mt.pt.y = (short)HIWORD(msg.lParam);
2758 /* Find a menu for this mouse event */
2759 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2763 /* no WM_NC... messages in captured state */
2765 case WM_RBUTTONDBLCLK:
2766 case WM_RBUTTONDOWN:
2767 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2769 case WM_LBUTTONDBLCLK:
2770 case WM_LBUTTONDOWN:
2771 /* If the message belongs to the menu, removes it from the queue */
2772 /* Else, end menu tracking */
2773 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2774 fEndMenu = !fRemove;
2778 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2781 /* Check if a menu was selected by the mouse */
2784 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2786 /* End the loop if executedMenuId is an item ID */
2787 /* or if the job was done (executedMenuId = 0). */
2788 fEndMenu = fRemove = (executedMenuId != -1);
2790 /* No menu was selected by the mouse */
2791 /* if the function was called by TrackPopupMenu, continue
2792 with the menu tracking. If not, stop it */
2794 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2799 /* the selected menu item must be changed every time */
2800 /* the mouse moves. */
2803 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2805 } /* switch(msg.message) - mouse */
2807 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2809 fRemove = TRUE; /* Keyboard messages are always removed */
2822 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2823 NO_SELECTED_ITEM, FALSE, 0 );
2826 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2827 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2830 case VK_DOWN: /* If on menu bar, pull-down the menu */
2832 menu = MENU_GetMenu( mt.hCurrentMenu );
2833 if (!(menu->wFlags & MF_POPUP))
2834 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2835 else /* otherwise try to move selection */
2836 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2840 MENU_KeyLeft( &mt, wFlags );
2844 MENU_KeyRight( &mt, wFlags );
2848 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2854 hi.cbSize = sizeof(HELPINFO);
2855 hi.iContextType = HELPINFO_MENUITEM;
2856 if (menu->FocusedItem == NO_SELECTED_ITEM)
2859 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2860 hi.hItemHandle = hmenu;
2861 hi.dwContextId = menu->dwContextHelpID;
2862 hi.MousePos = msg.pt;
2863 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2870 break; /* WM_KEYDOWN */
2877 if (msg.wParam == '\r' || msg.wParam == ' ')
2879 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2880 fEndMenu = (executedMenuId != -1);
2885 /* Hack to avoid control chars. */
2886 /* We will find a better way real soon... */
2887 if (msg.wParam < 32) break;
2889 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2890 LOWORD(msg.wParam), FALSE );
2891 if (pos == (UINT)-2) fEndMenu = TRUE;
2892 else if (pos == (UINT)-1) MessageBeep(0);
2895 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2897 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2898 fEndMenu = (executedMenuId != -1);
2902 } /* switch(msg.message) - kbd */
2906 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2907 DispatchMessageW( &msg );
2911 if (!fEndMenu) fRemove = TRUE;
2913 /* finally remove message from the queue */
2915 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2916 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2917 else mt.trackFlags &= ~TF_SKIPREMOVE;
2920 MENU_SetCapture(0); /* release the capture */
2922 /* If dropdown is still painted and the close box is clicked on
2923 then the menu will be destroyed as part of the DispatchMessage above.
2924 This will then invalidate the menu handle in mt.hTopMenu. We should
2925 check for this first. */
2926 if( IsMenu( mt.hTopMenu ) )
2928 menu = MENU_GetMenu( mt.hTopMenu );
2930 if( IsWindow( mt.hOwnerWnd ) )
2932 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2934 if (menu && (menu->wFlags & MF_POPUP))
2936 DestroyWindow( menu->hWnd );
2939 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2940 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2943 /* Reset the variable for hiding menu */
2944 if( menu ) menu->bTimeToHide = FALSE;
2947 /* The return value is only used by TrackPopupMenu */
2948 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2949 if (executedMenuId == -1) executedMenuId = 0;
2950 return executedMenuId;
2953 /***********************************************************************
2956 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2960 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2964 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2965 if (!(wFlags & TPM_NONOTIFY))
2966 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2968 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2970 if (!(wFlags & TPM_NONOTIFY))
2972 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2973 /* If an app changed/recreated menu bar entries in WM_INITMENU
2974 * menu sizes will be recalculated once the menu created/shown.
2978 /* This makes the menus of applications built with Delphi work.
2979 * It also enables menus to be displayed in more than one window,
2980 * but there are some bugs left that need to be fixed in this case.
2982 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2986 /***********************************************************************
2989 static BOOL MENU_ExitTracking(HWND hWnd)
2991 TRACE("hwnd=%p\n", hWnd);
2993 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2999 /***********************************************************************
3000 * MENU_TrackMouseMenuBar
3002 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3004 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3006 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3007 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3009 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3013 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3014 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3015 MENU_ExitTracking(hWnd);
3020 /***********************************************************************
3021 * MENU_TrackKbdMenuBar
3023 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3025 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3027 UINT uItem = NO_SELECTED_ITEM;
3029 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3031 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3033 /* find window that has a menu */
3035 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3036 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3038 /* check if we have to track a system menu */
3040 hTrackMenu = GetMenu( hwnd );
3041 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3043 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3044 hTrackMenu = get_win_sys_menu( hwnd );
3046 wParam |= HTSYSMENU; /* prevent item lookup */
3049 if (!IsMenu( hTrackMenu )) return;
3051 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3053 if( wChar && wChar != ' ' )
3055 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3056 if ( uItem >= (UINT)(-2) )
3058 if( uItem == (UINT)(-1) ) MessageBeep(0);
3059 /* schedule end of menu tracking */
3060 wFlags |= TF_ENDMENU;
3065 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3067 if (wParam & HTSYSMENU)
3069 /* prevent sysmenu activation for managed windows on Alt down/up */
3070 if (GetPropA( hwnd, "__wine_x11_managed" ))
3071 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3075 if( uItem == NO_SELECTED_ITEM )
3076 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3078 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3082 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3083 MENU_ExitTracking( hwnd );
3087 /**********************************************************************
3088 * TrackPopupMenu (USER32.@)
3090 * Like the win32 API, the function return the command ID only if the
3091 * flag TPM_RETURNCMD is on.
3094 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3095 INT nReserved, HWND hWnd, const RECT *lpRect )
3099 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3101 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3102 if (!(wFlags & TPM_NONOTIFY))
3103 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3105 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3106 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3107 MENU_ExitTracking(hWnd);
3112 /**********************************************************************
3113 * TrackPopupMenuEx (USER32.@)
3115 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3116 HWND hWnd, LPTPMPARAMS lpTpm )
3118 FIXME("not fully implemented\n" );
3119 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3120 lpTpm ? &lpTpm->rcExclude : NULL );
3123 /***********************************************************************
3126 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3128 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3130 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3136 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3137 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3141 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3142 return MA_NOACTIVATE;
3147 BeginPaint( hwnd, &ps );
3148 MENU_DrawPopupMenu( hwnd, ps.hdc,
3149 (HMENU)GetWindowLongW( hwnd, 0 ) );
3150 EndPaint( hwnd, &ps );
3157 /* zero out global pointer in case resident popup window was destroyed. */
3158 if (hwnd == top_popup) top_popup = 0;
3165 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3168 SetWindowLongW( hwnd, 0, 0 );
3171 case MM_SETMENUHANDLE:
3172 SetWindowLongW( hwnd, 0, wParam );
3175 case MM_GETMENUHANDLE:
3176 return GetWindowLongW( hwnd, 0 );
3179 return DefWindowProcW( hwnd, message, wParam, lParam );
3185 /***********************************************************************
3186 * MENU_GetMenuBarHeight
3188 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3190 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3191 INT orgX, INT orgY )
3197 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3199 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3201 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3202 SelectObject( hdc, get_menu_font(FALSE));
3203 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3204 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3205 ReleaseDC( hwnd, hdc );
3206 return lppop->Height;
3210 /*******************************************************************
3211 * ChangeMenuA (USER32.@)
3213 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3214 UINT id, UINT flags )
3216 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3217 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3219 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3220 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3222 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3223 flags & MF_BYPOSITION ? pos : id,
3224 flags & ~MF_REMOVE );
3225 /* Default: MF_INSERT */
3226 return InsertMenuA( hMenu, pos, flags, id, data );
3230 /*******************************************************************
3231 * ChangeMenuW (USER32.@)
3233 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3234 UINT id, UINT flags )
3236 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3237 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3239 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3240 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3242 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3243 flags & MF_BYPOSITION ? pos : id,
3244 flags & ~MF_REMOVE );
3245 /* Default: MF_INSERT */
3246 return InsertMenuW( hMenu, pos, flags, id, data );
3250 /*******************************************************************
3251 * CheckMenuItem (USER32.@)
3253 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3258 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3259 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3260 ret = item->fState & MF_CHECKED;
3261 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3262 else item->fState &= ~MF_CHECKED;
3267 /**********************************************************************
3268 * EnableMenuItem (USER32.@)
3270 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3276 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3278 /* Get the Popupmenu to access the owner menu */
3279 if (!(menu = MENU_GetMenu(hMenu)))
3282 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3285 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3286 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3288 /* If the close item in the system menu change update the close button */
3289 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3291 if (menu->hSysMenuOwner != 0)
3294 POPUPMENU* parentMenu;
3296 /* Get the parent menu to access*/
3297 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3300 /* Refresh the frame to reflect the change */
3301 GetWindowRect(parentMenu->hWnd, &rc);
3302 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3304 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3312 /*******************************************************************
3313 * GetMenuStringA (USER32.@)
3315 INT WINAPI GetMenuStringA(
3316 HMENU hMenu, /* [in] menuhandle */
3317 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3318 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3319 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3320 UINT wFlags /* [in] MF_ flags */
3324 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3325 if (str && nMaxSiz) str[0] = '\0';
3326 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3327 if (!IS_STRING_ITEM(item->fType)) return 0;
3328 if (!str || !nMaxSiz) return strlenW(item->text);
3329 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3331 TRACE("returning '%s'\n", str );
3336 /*******************************************************************
3337 * GetMenuStringW (USER32.@)
3339 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3340 LPWSTR str, INT nMaxSiz, UINT wFlags )
3344 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3345 if (str && nMaxSiz) str[0] = '\0';
3346 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3347 if (!IS_STRING_ITEM(item->fType)) return 0;
3348 if (!str || !nMaxSiz) return strlenW(item->text);
3349 lstrcpynW( str, item->text, nMaxSiz );
3350 return strlenW(str);
3354 /**********************************************************************
3355 * HiliteMenuItem (USER32.@)
3357 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3361 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3362 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3363 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3364 if (menu->FocusedItem == wItemID) return TRUE;
3365 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3366 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3371 /**********************************************************************
3372 * GetMenuState (USER32.@)
3374 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3377 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3378 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3379 debug_print_menuitem (" item: ", item, "");
3380 if (item->fType & MF_POPUP)
3382 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3383 if (!menu) return -1;
3384 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3388 /* We used to (from way back then) mask the result to 0xff. */
3389 /* I don't know why and it seems wrong as the documented */
3390 /* return flag MF_SEPARATOR is outside that mask. */
3391 return (item->fType | item->fState);
3396 /**********************************************************************
3397 * GetMenuItemCount (USER32.@)
3399 INT WINAPI GetMenuItemCount( HMENU hMenu )
3401 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3402 if (!menu) return -1;
3403 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3404 return menu->nItems;
3408 /**********************************************************************
3409 * GetMenuItemID (USER32.@)
3411 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3415 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3416 if (lpmi->fType & MF_POPUP) return -1;
3422 /*******************************************************************
3423 * InsertMenuW (USER32.@)
3425 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3426 UINT_PTR id, LPCWSTR str )
3430 if (IS_STRING_ITEM(flags) && str)
3431 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3432 hMenu, pos, flags, id, debugstr_w(str) );
3433 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3434 hMenu, pos, flags, id, (DWORD)str );
3436 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3438 if (!(MENU_SetItemData( item, flags, id, str )))
3440 RemoveMenu( hMenu, pos, flags );
3444 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3445 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3447 item->hCheckBit = item->hUnCheckBit = 0;
3452 /*******************************************************************
3453 * InsertMenuA (USER32.@)
3455 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3456 UINT_PTR id, LPCSTR str )
3460 if (IS_STRING_ITEM(flags) && str)
3462 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3463 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3466 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3467 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3468 HeapFree( GetProcessHeap(), 0, newstr );
3472 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3476 /*******************************************************************
3477 * AppendMenuA (USER32.@)
3479 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3480 UINT_PTR id, LPCSTR data )
3482 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3486 /*******************************************************************
3487 * AppendMenuW (USER32.@)
3489 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3490 UINT_PTR id, LPCWSTR data )
3492 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3496 /**********************************************************************
3497 * RemoveMenu (USER32.@)
3499 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3504 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3505 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3506 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3510 MENU_FreeItemData( item );
3512 if (--menu->nItems == 0)
3514 HeapFree( GetProcessHeap(), 0, menu->items );
3519 while(nPos < menu->nItems)
3525 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3526 menu->nItems * sizeof(MENUITEM) );
3532 /**********************************************************************
3533 * DeleteMenu (USER32.@)
3535 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3537 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3538 if (!item) return FALSE;
3539 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3540 /* nPos is now the position of the item */
3541 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3546 /*******************************************************************
3547 * ModifyMenuW (USER32.@)
3549 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3550 UINT_PTR id, LPCWSTR str )
3554 if (IS_STRING_ITEM(flags))
3556 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3560 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3563 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3564 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3565 return MENU_SetItemData( item, flags, id, str );
3569 /*******************************************************************
3570 * ModifyMenuA (USER32.@)
3572 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3573 UINT_PTR id, LPCSTR str )
3577 if (IS_STRING_ITEM(flags) && str)
3579 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3580 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3583 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3584 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3585 HeapFree( GetProcessHeap(), 0, newstr );
3589 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3593 /**********************************************************************
3594 * CreatePopupMenu (USER32.@)
3596 HMENU WINAPI CreatePopupMenu(void)
3601 if (!(hmenu = CreateMenu())) return 0;
3602 menu = MENU_GetMenu( hmenu );
3603 menu->wFlags |= MF_POPUP;
3604 menu->bTimeToHide = FALSE;
3609 /**********************************************************************
3610 * GetMenuCheckMarkDimensions (USER.417)
3611 * GetMenuCheckMarkDimensions (USER32.@)
3613 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3615 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3619 /**********************************************************************
3620 * SetMenuItemBitmaps (USER32.@)
3622 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3623 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3626 TRACE("(%p, %04x, %04x, %p, %p)\n",
3627 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3628 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3630 if (!hNewCheck && !hNewUnCheck)
3632 item->fState &= ~MF_USECHECKBITMAPS;
3634 else /* Install new bitmaps */
3636 item->hCheckBit = hNewCheck;
3637 item->hUnCheckBit = hNewUnCheck;
3638 item->fState |= MF_USECHECKBITMAPS;
3644 /**********************************************************************
3645 * CreateMenu (USER32.@)
3647 HMENU WINAPI CreateMenu(void)
3651 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3652 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3654 ZeroMemory(menu, sizeof(POPUPMENU));
3655 menu->wMagic = MENU_MAGIC;
3656 menu->FocusedItem = NO_SELECTED_ITEM;
3657 menu->bTimeToHide = FALSE;
3659 TRACE("return %p\n", hMenu );
3665 /**********************************************************************
3666 * DestroyMenu (USER32.@)
3668 BOOL WINAPI DestroyMenu( HMENU hMenu )
3670 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3672 TRACE("(%p)\n", hMenu);
3675 if (!lppop) return FALSE;
3677 lppop->wMagic = 0; /* Mark it as destroyed */
3679 /* DestroyMenu should not destroy system menu popup owner */
3680 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3682 DestroyWindow( lppop->hWnd );
3686 if (lppop->items) /* recursively destroy submenus */
3689 MENUITEM *item = lppop->items;
3690 for (i = lppop->nItems; i > 0; i--, item++)
3692 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3693 MENU_FreeItemData( item );
3695 HeapFree( GetProcessHeap(), 0, lppop->items );
3697 USER_HEAP_FREE( hMenu );
3702 /**********************************************************************
3703 * GetSystemMenu (USER32.@)
3705 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3707 WND *wndPtr = WIN_GetPtr( hWnd );
3710 if (wndPtr == WND_DESKTOP) return 0;
3711 if (wndPtr == WND_OTHER_PROCESS)
3713 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3717 if (wndPtr->hSysMenu && bRevert)
3719 DestroyMenu(wndPtr->hSysMenu);
3720 wndPtr->hSysMenu = 0;
3723 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3724 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3726 if( wndPtr->hSysMenu )
3729 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3731 /* Store the dummy sysmenu handle to facilitate the refresh */
3732 /* of the close button if the SC_CLOSE item change */
3733 menu = MENU_GetMenu(retvalue);
3735 menu->hSysMenuOwner = wndPtr->hSysMenu;
3737 WIN_ReleasePtr( wndPtr );
3739 return bRevert ? 0 : retvalue;
3743 /*******************************************************************
3744 * SetSystemMenu (USER32.@)
3746 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3748 WND *wndPtr = WIN_GetPtr( hwnd );
3750 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3752 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3753 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3754 WIN_ReleasePtr( wndPtr );
3761 /**********************************************************************
3762 * GetMenu (USER32.@)
3764 HMENU WINAPI GetMenu( HWND hWnd )
3766 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3767 TRACE("for %p returning %p\n", hWnd, retvalue);
3771 /**********************************************************************
3772 * GetMenuBarInfo (USER32.@)
3774 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3776 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
3780 /**********************************************************************
3783 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3784 * SetWindowPos call that would result if SetMenu were called directly.
3786 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3788 TRACE("(%p, %p);\n", hWnd, hMenu);
3790 if (hMenu && !IsMenu(hMenu))
3792 WARN("hMenu %p is not a menu handle\n", hMenu);
3793 SetLastError(ERROR_INVALID_MENU_HANDLE);
3796 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3799 hWnd = WIN_GetFullHandle( hWnd );
3800 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3806 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3808 lpmenu->hWnd = hWnd;
3809 lpmenu->Height = 0; /* Make sure we recalculate the size */
3811 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3816 /**********************************************************************
3817 * SetMenu (USER32.@)
3819 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3821 if(!MENU_SetMenu(hWnd, hMenu))
3824 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3825 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3830 /**********************************************************************
3831 * GetSubMenu (USER32.@)
3833 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3837 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3838 if (!(lpmi->fType & MF_POPUP)) return 0;
3839 return lpmi->hSubMenu;
3843 /**********************************************************************
3844 * DrawMenuBar (USER32.@)
3846 BOOL WINAPI DrawMenuBar( HWND hWnd )
3849 HMENU hMenu = GetMenu(hWnd);
3851 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3853 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3855 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3856 lppop->hwndOwner = hWnd;
3857 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3858 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3862 /***********************************************************************
3863 * DrawMenuBarTemp (USER32.@)
3867 * called by W98SE desk.cpl Control Panel Applet
3869 * Not 100% sure about the param names, but close.
3871 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3876 BOOL flat_menu = FALSE;
3878 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
3881 hMenu = GetMenu(hwnd);
3884 hFont = get_menu_font(FALSE);
3886 lppop = MENU_GetMenu( hMenu );
3887 if (lppop == NULL || lprect == NULL)
3889 retvalue = GetSystemMetrics(SM_CYMENU);
3893 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3895 hfontOld = SelectObject( hDC, hFont);
3897 if (lppop->Height == 0)
3898 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3900 lprect->bottom = lprect->top + lppop->Height;
3902 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
3904 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3905 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3906 LineTo( hDC, lprect->right, lprect->bottom );
3908 if (lppop->nItems == 0)
3910 retvalue = GetSystemMetrics(SM_CYMENU);
3914 for (i = 0; i < lppop->nItems; i++)
3916 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3917 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3919 retvalue = lppop->Height;
3922 if (hfontOld) SelectObject (hDC, hfontOld);
3926 /***********************************************************************
3927 * EndMenu (USER.187)
3928 * EndMenu (USER32.@)
3930 void WINAPI EndMenu(void)
3932 /* if we are in the menu code, and it is active */
3933 if (!fEndMenu && top_popup)
3935 /* terminate the menu handling code */
3938 /* needs to be posted to wakeup the internal menu handler */
3939 /* which will now terminate the menu, in the event that */
3940 /* the main window was minimized, or lost focus, so we */
3941 /* don't end up with an orphaned menu */
3942 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3947 /***********************************************************************
3948 * LookupMenuHandle (USER.217)
3950 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3952 HMENU hmenu32 = HMENU_32(hmenu);
3954 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3955 else return HMENU_16(hmenu32);
3959 /**********************************************************************
3960 * LoadMenu (USER.150)
3962 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3968 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3969 if (!name) return 0;
3971 instance = GetExePtr( instance );
3972 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3973 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3974 hMenu = LoadMenuIndirect16(LockResource16(handle));
3975 FreeResource16( handle );
3980 /*****************************************************************
3981 * LoadMenuA (USER32.@)
3983 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3985 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3986 if (!hrsrc) return 0;
3987 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3991 /*****************************************************************
3992 * LoadMenuW (USER32.@)
3994 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3996 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3997 if (!hrsrc) return 0;
3998 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4002 /**********************************************************************
4003 * LoadMenuIndirect (USER.220)
4005 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4008 WORD version, offset;
4009 LPCSTR p = (LPCSTR)template;
4011 TRACE("(%p)\n", template );
4012 version = GET_WORD(p);
4016 WARN("version must be 0 for Win16\n" );
4019 offset = GET_WORD(p);
4020 p += sizeof(WORD) + offset;
4021 if (!(hMenu = CreateMenu())) return 0;
4022 if (!MENU_ParseResource( p, hMenu, FALSE ))
4024 DestroyMenu( hMenu );
4027 return HMENU_16(hMenu);
4031 /**********************************************************************
4032 * LoadMenuIndirectW (USER32.@)
4034 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4037 WORD version, offset;
4038 LPCSTR p = (LPCSTR)template;
4040 version = GET_WORD(p);
4042 TRACE("%p, ver %d\n", template, version );
4045 case 0: /* standard format is version of 0 */
4046 offset = GET_WORD(p);
4047 p += sizeof(WORD) + offset;
4048 if (!(hMenu = CreateMenu())) return 0;
4049 if (!MENU_ParseResource( p, hMenu, TRUE ))
4051 DestroyMenu( hMenu );
4055 case 1: /* extended format is version of 1 */
4056 offset = GET_WORD(p);
4057 p += sizeof(WORD) + offset;
4058 if (!(hMenu = CreateMenu())) return 0;
4059 if (!MENUEX_ParseResource( p, hMenu))
4061 DestroyMenu( hMenu );
4066 ERR("version %d not supported.\n", version);
4072 /**********************************************************************
4073 * LoadMenuIndirectA (USER32.@)
4075 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4077 return LoadMenuIndirectW( template );
4081 /**********************************************************************
4084 BOOL WINAPI IsMenu(HMENU hmenu)
4086 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4087 return menu != NULL;
4090 /**********************************************************************
4091 * GetMenuItemInfo_common
4094 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4095 LPMENUITEMINFOW lpmii, BOOL unicode)
4097 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4099 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4104 if (lpmii->fMask & MIIM_TYPE) {
4105 lpmii->fType = menu->fType;
4106 switch (MENU_ITEM_TYPE(menu->fType)) {
4108 break; /* will be done below */
4111 lpmii->dwTypeData = menu->text;
4118 /* copy the text string */
4119 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4120 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4125 len = strlenW(menu->text);
4126 if(lpmii->dwTypeData && lpmii->cch)
4127 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4131 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4132 if(lpmii->dwTypeData && lpmii->cch)
4133 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4134 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4135 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4137 /* if we've copied a substring we return its length */
4138 if(lpmii->dwTypeData && lpmii->cch)
4140 if (lpmii->cch <= len) lpmii->cch--;
4142 else /* return length of string */
4146 if (lpmii->fMask & MIIM_FTYPE)
4147 lpmii->fType = menu->fType;
4149 if (lpmii->fMask & MIIM_BITMAP)
4150 lpmii->hbmpItem = menu->hbmpItem;
4152 if (lpmii->fMask & MIIM_STATE)
4153 lpmii->fState = menu->fState;
4155 if (lpmii->fMask & MIIM_ID)
4156 lpmii->wID = menu->wID;
4158 if (lpmii->fMask & MIIM_SUBMENU)
4159 lpmii->hSubMenu = menu->hSubMenu;
4161 if (lpmii->fMask & MIIM_CHECKMARKS) {
4162 lpmii->hbmpChecked = menu->hCheckBit;
4163 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4165 if (lpmii->fMask & MIIM_DATA)
4166 lpmii->dwItemData = menu->dwItemData;
4171 /**********************************************************************
4172 * GetMenuItemInfoA (USER32.@)
4174 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4175 LPMENUITEMINFOA lpmii)
4177 return GetMenuItemInfo_common (hmenu, item, bypos,
4178 (LPMENUITEMINFOW)lpmii, FALSE);
4181 /**********************************************************************
4182 * GetMenuItemInfoW (USER32.@)
4184 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4185 LPMENUITEMINFOW lpmii)
4187 return GetMenuItemInfo_common (hmenu, item, bypos,
4192 /* set a menu item text from a ASCII or Unicode string */
4193 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4198 menu->fType |= MF_SEPARATOR;
4202 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4203 strcpyW( menu->text, text );
4207 LPCSTR str = (LPCSTR)text;
4208 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4209 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4210 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4215 /**********************************************************************
4216 * SetMenuItemInfo_common
4219 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4220 const MENUITEMINFOW *lpmii,
4223 if (!menu) return FALSE;
4225 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4227 if (lpmii->fMask & MIIM_TYPE ) {
4228 /* Get rid of old string. */
4229 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4230 HeapFree(GetProcessHeap(), 0, menu->text);
4234 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4235 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4236 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4238 menu->text = lpmii->dwTypeData;
4240 if (IS_STRING_ITEM(menu->fType))
4241 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4244 if (lpmii->fMask & MIIM_FTYPE ) {
4245 /* free the string when the type is changing */
4246 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4247 HeapFree(GetProcessHeap(), 0, menu->text);
4250 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4251 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4252 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4253 menu->fType |= MF_SEPARATOR;
4256 if (lpmii->fMask & MIIM_STRING ) {
4257 if (IS_STRING_ITEM(menu->fType)) {
4258 /* free the string when used */
4259 HeapFree(GetProcessHeap(), 0, menu->text);
4260 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4264 if (lpmii->fMask & MIIM_STATE)
4266 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4267 menu->fState = lpmii->fState;
4270 if (lpmii->fMask & MIIM_ID)
4271 menu->wID = lpmii->wID;
4273 if (lpmii->fMask & MIIM_SUBMENU) {
4274 menu->hSubMenu = lpmii->hSubMenu;
4275 if (menu->hSubMenu) {
4276 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4278 subMenu->wFlags |= MF_POPUP;
4279 menu->fType |= MF_POPUP;
4282 /* FIXME: Return an error ? */
4283 menu->fType &= ~MF_POPUP;
4286 menu->fType &= ~MF_POPUP;
4289 if (lpmii->fMask & MIIM_CHECKMARKS)
4291 if (lpmii->fType & MFT_RADIOCHECK)
4292 menu->fType |= MFT_RADIOCHECK;
4294 menu->hCheckBit = lpmii->hbmpChecked;
4295 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4297 if (lpmii->fMask & MIIM_DATA)
4298 menu->dwItemData = lpmii->dwItemData;
4300 if (lpmii->fMask & MIIM_BITMAP)
4301 menu->hbmpItem = lpmii->hbmpItem;
4303 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4307 /**********************************************************************
4308 * SetMenuItemInfoA (USER32.@)
4310 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4311 const MENUITEMINFOA *lpmii)
4313 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4314 (const MENUITEMINFOW *)lpmii, FALSE);
4317 /**********************************************************************
4318 * SetMenuItemInfoW (USER32.@)
4320 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4321 const MENUITEMINFOW *lpmii)
4323 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4327 /**********************************************************************
4328 * SetMenuDefaultItem (USER32.@)
4331 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4337 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4339 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4341 /* reset all default-item flags */
4343 for (i = 0; i < menu->nItems; i++, item++)
4345 item->fState &= ~MFS_DEFAULT;
4348 /* no default item */
4357 if ( uItem >= menu->nItems ) return FALSE;
4358 item[uItem].fState |= MFS_DEFAULT;
4363 for (i = 0; i < menu->nItems; i++, item++)
4365 if (item->wID == uItem)
4367 item->fState |= MFS_DEFAULT;
4376 /**********************************************************************
4377 * GetMenuDefaultItem (USER32.@)
4379 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4385 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4387 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4389 /* find default item */
4393 if (! item) return -1;
4395 while ( !( item->fState & MFS_DEFAULT ) )
4398 if (i >= menu->nItems ) return -1;
4401 /* default: don't return disabled items */
4402 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4404 /* search rekursiv when needed */
4405 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4408 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4409 if ( -1 != ret ) return ret;
4411 /* when item not found in submenu, return the popup item */
4413 return ( bypos ) ? i : item->wID;
4418 /**********************************************************************
4419 * InsertMenuItemA (USER32.@)
4421 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4422 const MENUITEMINFOA *lpmii)
4424 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4425 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4429 /**********************************************************************
4430 * InsertMenuItemW (USER32.@)
4432 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4433 const MENUITEMINFOW *lpmii)
4435 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4436 return SetMenuItemInfo_common(item, lpmii, TRUE);
4439 /**********************************************************************
4440 * CheckMenuRadioItem (USER32.@)
4443 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4444 UINT first, UINT last, UINT check,
4447 MENUITEM *mifirst, *milast, *micheck;
4448 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4450 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4452 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4453 milast = MENU_FindItem (&mlast, &last, bypos);
4454 micheck = MENU_FindItem (&mcheck, &check, bypos);
4456 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4457 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4458 micheck > milast || micheck < mifirst)
4461 while (mifirst <= milast)
4463 if (mifirst == micheck)
4465 mifirst->fType |= MFT_RADIOCHECK;
4466 mifirst->fState |= MFS_CHECKED;
4468 mifirst->fType &= ~MFT_RADIOCHECK;
4469 mifirst->fState &= ~MFS_CHECKED;
4478 /**********************************************************************
4479 * GetMenuItemRect (USER32.@)
4481 * ATTENTION: Here, the returned values in rect are the screen
4482 * coordinates of the item just like if the menu was
4483 * always on the upper left side of the application.
4486 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4489 POPUPMENU *itemMenu;
4493 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4495 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4496 referenceHwnd = hwnd;
4500 itemMenu = MENU_GetMenu(hMenu);
4501 if (itemMenu == NULL)
4504 if(itemMenu->hWnd == 0)
4506 referenceHwnd = itemMenu->hWnd;
4509 if ((rect == NULL) || (item == NULL))
4514 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4520 /**********************************************************************
4521 * SetMenuInfo (USER32.@)
4524 * MIM_APPLYTOSUBMENUS
4525 * actually use the items to draw the menu
4527 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4531 TRACE("(%p %p)\n", hMenu, lpmi);
4533 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4536 if (lpmi->fMask & MIM_BACKGROUND)
4537 menu->hbrBack = lpmi->hbrBack;
4539 if (lpmi->fMask & MIM_HELPID)
4540 menu->dwContextHelpID = lpmi->dwContextHelpID;
4542 if (lpmi->fMask & MIM_MAXHEIGHT)
4543 menu->cyMax = lpmi->cyMax;
4545 if (lpmi->fMask & MIM_MENUDATA)
4546 menu->dwMenuData = lpmi->dwMenuData;
4548 if (lpmi->fMask & MIM_STYLE)
4550 menu->dwStyle = lpmi->dwStyle;
4551 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4552 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4553 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4554 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4555 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4563 /**********************************************************************
4564 * GetMenuInfo (USER32.@)
4570 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4573 TRACE("(%p %p)\n", hMenu, lpmi);
4575 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4578 if (lpmi->fMask & MIM_BACKGROUND)
4579 lpmi->hbrBack = menu->hbrBack;
4581 if (lpmi->fMask & MIM_HELPID)
4582 lpmi->dwContextHelpID = menu->dwContextHelpID;
4584 if (lpmi->fMask & MIM_MAXHEIGHT)
4585 lpmi->cyMax = menu->cyMax;
4587 if (lpmi->fMask & MIM_MENUDATA)
4588 lpmi->dwMenuData = menu->dwMenuData;
4590 if (lpmi->fMask & MIM_STYLE)
4591 lpmi->dwStyle = menu->dwStyle;
4599 /**********************************************************************
4600 * SetMenuContextHelpId (USER32.@)
4602 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4606 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4608 if ((menu = MENU_GetMenu(hMenu)))
4610 menu->dwContextHelpID = dwContextHelpID;
4617 /**********************************************************************
4618 * GetMenuContextHelpId (USER32.@)
4620 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4624 TRACE("(%p)\n", hMenu);
4626 if ((menu = MENU_GetMenu(hMenu)))
4628 return menu->dwContextHelpID;
4633 /**********************************************************************
4634 * MenuItemFromPoint (USER32.@)
4636 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4638 POPUPMENU *menu = MENU_GetMenu(hMenu);
4641 /*FIXME: Do we have to handle hWnd here? */
4642 if (!menu) return -1;
4643 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4648 /**********************************************************************
4649 * translate_accelerator
4651 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4652 BYTE fVirt, WORD key, WORD cmd )
4657 if (wParam != key) return FALSE;
4659 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4660 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4661 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4663 if (message == WM_CHAR || message == WM_SYSCHAR)
4665 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4667 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4673 if(fVirt & FVIRTKEY)
4675 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4676 wParam, 0xff & HIWORD(lParam));
4678 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4679 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4683 if (!(lParam & 0x01000000)) /* no special_key */
4685 if ((fVirt & FALT) && (lParam & 0x20000000))
4686 { /* ^^ ALT pressed */
4687 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4696 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4700 HMENU hMenu, hSubMenu, hSysMenu;
4701 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4703 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4704 hSysMenu = get_win_sys_menu( hWnd );
4706 /* find menu item and ask application to initialize it */
4707 /* 1. in the system menu */
4708 hSubMenu = hSysMenu;
4710 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4714 if (!IsWindowEnabled(hWnd))
4718 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4719 if(hSubMenu != hSysMenu)
4721 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4722 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4723 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4725 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4728 else /* 2. in the window's menu */
4732 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4736 if (!IsWindowEnabled(hWnd))
4740 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4741 if(hSubMenu != hMenu)
4743 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4744 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4745 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4747 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4754 if (uSysStat != (UINT)-1)
4756 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4763 if (uStat != (UINT)-1)
4769 if (uStat & (MF_DISABLED|MF_GRAYED))
4781 if( mesg==WM_COMMAND )
4783 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4784 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4786 else if( mesg==WM_SYSCOMMAND )
4788 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4789 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4793 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4794 * #0: unknown (please report!)
4795 * #1: for WM_KEYUP,WM_SYSKEYUP
4796 * #2: mouse is captured
4797 * #3: window is disabled
4798 * #4: it's a disabled system menu option
4799 * #5: it's a menu option, but window is iconic
4800 * #6: it's a menu option, but disabled
4802 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4804 ERR_(accel)(" unknown reason - please report!\n");
4809 /**********************************************************************
4810 * TranslateAccelerator (USER32.@)
4811 * TranslateAcceleratorA (USER32.@)
4813 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4816 LPACCEL16 lpAccelTbl;
4820 if (!hWnd || !msg) return 0;
4822 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4824 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4828 wParam = msg->wParam;
4830 switch (msg->message)
4839 char ch = LOWORD(wParam);
4841 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4842 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4850 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4851 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4855 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4856 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4858 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4863 /**********************************************************************
4864 * TranslateAcceleratorW (USER32.@)
4866 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4869 LPACCEL16 lpAccelTbl;
4872 if (!hWnd || !msg) return 0;
4874 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4876 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4880 switch (msg->message)
4892 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4893 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4897 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4898 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4900 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);