4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Note: the style MF_MOUSESELECT is used to mark popup items that
26 * have been selected, i.e. their popup menu is currently displayed.
27 * This is probably not the meaning this style has in MS-Windows.
39 #include "wine/port.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* internal popup menu window messages */
63 #define MM_SETMENUHANDLE (WM_USER + 0)
64 #define MM_GETMENUHANDLE (WM_USER + 1)
66 /* Menu item structure */
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT_PTR wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
84 /* Popup menu structure */
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103 SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
106 /* internal flags for menu tracking */
108 #define TF_ENDMENU 0x0001
109 #define TF_SUSPENDPOPUP 0x0002
110 #define TF_SKIPREMOVE 0x0004
115 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116 HMENU hTopMenu; /* initial menu */
117 HWND hOwnerWnd; /* where notifications are sent */
121 #define MENU_MAGIC 0x554d /* 'MU' */
126 /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL 0xF0000000
128 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* popup menu shade thickness */
133 #define POPUP_XSHADE 4
134 #define POPUP_YSHADE 4
136 /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
139 /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
142 /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
152 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
153 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
159 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
160 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
161 MF_POPUP | MF_SYSMENU | MF_HELP)
162 #define STATE_MASK (~TYPE_MASK)
164 /* Dimension of the menu bitmaps */
165 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
167 static HBITMAP hStdMnArrow = 0;
168 static HBITMAP hBmpSysMenu = 0;
170 static HBRUSH hShadeBrush = 0;
171 static HFONT hMenuFont = 0;
172 static HFONT hMenuFontBold = 0;
174 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class =
192 POPUPMENU_CLASS_ATOMA, /* name */
193 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
194 NULL, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc, /* procW */
196 sizeof(HMENU), /* extra */
197 IDC_ARROW, /* cursor */
198 (HBRUSH)(COLOR_MENU+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
211 #define MENUOUT(text) \
212 DPRINTF("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
222 TRACE("%s ", prefix);
224 UINT flags = mp->fType;
225 int type = MENU_ITEM_TYPE(flags);
226 DPRINTF( "{ ID=0x%x", mp->wID);
227 if (flags & MF_POPUP)
228 DPRINTF( ", Sub=%p", mp->hSubMenu);
232 if (type == MFT_STRING)
234 else if (type == MFT_SEPARATOR)
236 else if (type == MFT_OWNERDRAW)
238 else if (type == MFT_BITMAP)
244 MENUFLAG(MF_POPUP, "pop");
245 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
246 MENUFLAG(MFT_MENUBREAK, "brk");
247 MENUFLAG(MFT_RADIOCHECK, "radio");
248 MENUFLAG(MFT_RIGHTORDER, "rorder");
249 MENUFLAG(MF_SYSMENU, "sys");
250 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
253 DPRINTF( "+0x%x", flags);
258 DPRINTF( ", State=");
259 MENUFLAG(MFS_GRAYED, "grey");
260 MENUFLAG(MFS_DEFAULT, "default");
261 MENUFLAG(MFS_DISABLED, "dis");
262 MENUFLAG(MFS_CHECKED, "check");
263 MENUFLAG(MFS_HILITE, "hi");
264 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
265 MENUFLAG(MF_MOUSESELECT, "mouse");
267 DPRINTF( "+0x%x", flags);
270 DPRINTF( ", Chk=%p", mp->hCheckBit);
272 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
274 if (type == MFT_STRING) {
276 DPRINTF( ", Text=%s", debugstr_w(mp->text));
278 DPRINTF( ", Text=Null");
279 } else if (mp->text == NULL)
282 DPRINTF( ", Text=%p", mp->text);
284 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
290 DPRINTF(" %s\n", postfix);
297 /***********************************************************************
300 * Validate the given menu handle and returns the menu structure pointer.
302 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
304 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
305 if (!menu || menu->wMagic != MENU_MAGIC)
307 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
313 /***********************************************************************
316 * Get the system menu of a window
318 static HMENU get_win_sys_menu( HWND hwnd )
321 WND *win = WIN_GetPtr( hwnd );
322 if (win && win != WND_OTHER_PROCESS)
325 WIN_ReleasePtr( win );
330 /***********************************************************************
333 * Return the default system menu.
335 static HMENU MENU_CopySysPopup(void)
337 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
338 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
341 POPUPMENU* menu = MENU_GetMenu(hMenu);
342 menu->wFlags |= MF_SYSMENU | MF_POPUP;
343 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
346 ERR("Unable to load default system menu\n" );
348 TRACE("returning %p.\n", hMenu );
354 /**********************************************************************
357 * Create a copy of the system menu. System menu in Windows is
358 * a special menu bar with the single entry - system menu popup.
359 * This popup is presented to the outside world as a "system menu".
360 * However, the real system menu handle is sometimes seen in the
361 * WM_MENUSELECT parameters (and Word 6 likes it this way).
363 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
367 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
368 if ((hMenu = CreateMenu()))
370 POPUPMENU *menu = MENU_GetMenu(hMenu);
371 menu->wFlags = MF_SYSMENU;
372 menu->hWnd = WIN_GetFullHandle( hWnd );
373 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
375 if (hPopupMenu == (HMENU)(-1))
376 hPopupMenu = MENU_CopySysPopup();
377 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
381 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
382 (UINT_PTR)hPopupMenu, NULL );
384 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
385 menu->items[0].fState = 0;
386 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
388 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
391 DestroyMenu( hMenu );
393 ERR("failed to load system menu!\n");
398 /***********************************************************************
401 * Menus initialisation.
406 NONCLIENTMETRICSW ncm;
408 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
413 /* Load menu bitmaps */
414 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
415 /* Load system buttons bitmaps */
416 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
421 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
422 arrow_bitmap_width = bm.bmWidth;
423 arrow_bitmap_height = bm.bmHeight;
427 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
430 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
433 DeleteObject( hBitmap );
434 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
437 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
438 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
441 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
444 ncm.lfMenuFont.lfWeight += 300;
445 if ( ncm.lfMenuFont.lfWeight > 1000)
446 ncm.lfMenuFont.lfWeight = 1000;
448 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
454 /***********************************************************************
455 * MENU_InitSysMenuPopup
457 * Grey the appropriate items in System menu.
459 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
463 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
464 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
465 gray = ((style & WS_MAXIMIZE) != 0);
466 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
467 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
468 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
469 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
470 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
471 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
472 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
473 gray = (clsStyle & CS_NOCLOSE) != 0;
475 /* The menu item must keep its state if it's disabled */
477 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
481 /******************************************************************************
483 * UINT MENU_GetStartOfNextColumn(
486 *****************************************************************************/
488 static UINT MENU_GetStartOfNextColumn(
491 POPUPMENU *menu = MENU_GetMenu(hMenu);
495 return NO_SELECTED_ITEM;
497 i = menu->FocusedItem + 1;
498 if( i == NO_SELECTED_ITEM )
501 for( ; i < menu->nItems; ++i ) {
502 if (menu->items[i].fType & MF_MENUBARBREAK)
506 return NO_SELECTED_ITEM;
510 /******************************************************************************
512 * UINT MENU_GetStartOfPrevColumn(
515 *****************************************************************************/
517 static UINT MENU_GetStartOfPrevColumn(
520 POPUPMENU *menu = MENU_GetMenu(hMenu);
524 return NO_SELECTED_ITEM;
526 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
527 return NO_SELECTED_ITEM;
529 /* Find the start of the column */
531 for(i = menu->FocusedItem; i != 0 &&
532 !(menu->items[i].fType & MF_MENUBARBREAK);
536 return NO_SELECTED_ITEM;
538 for(--i; i != 0; --i) {
539 if (menu->items[i].fType & MF_MENUBARBREAK)
543 TRACE("ret %d.\n", i );
550 /***********************************************************************
553 * Find a menu item. Return a pointer on the item, and modifies *hmenu
554 * in case the item was in a sub-menu.
556 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
561 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
562 if (wFlags & MF_BYPOSITION)
564 if (*nPos >= menu->nItems) return NULL;
565 return &menu->items[*nPos];
569 MENUITEM *item = menu->items;
570 for (i = 0; i < menu->nItems; i++, item++)
572 if (item->wID == *nPos)
577 else if (item->fType & MF_POPUP)
579 HMENU hsubmenu = item->hSubMenu;
580 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
592 /***********************************************************************
595 * Find a Sub menu. Return the position of the submenu, and modifies
596 * *hmenu in case it is found in another sub-menu.
597 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
599 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
604 if (((*hmenu)==(HMENU)0xffff) ||
605 (!(menu = MENU_GetMenu(*hmenu))))
606 return NO_SELECTED_ITEM;
608 for (i = 0; i < menu->nItems; i++, item++) {
609 if(!(item->fType & MF_POPUP)) continue;
610 if (item->hSubMenu == hSubTarget) {
614 HMENU hsubmenu = item->hSubMenu;
615 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
616 if (pos != NO_SELECTED_ITEM) {
622 return NO_SELECTED_ITEM;
625 /***********************************************************************
628 static void MENU_FreeItemData( MENUITEM* item )
631 if (IS_STRING_ITEM(item->fType) && item->text)
632 HeapFree( GetProcessHeap(), 0, item->text );
635 /***********************************************************************
636 * MENU_FindItemByCoords
638 * Find the item at the specified coordinates (screen coords). Does
639 * not work for child windows and therefore should not be called for
640 * an arbitrary system menu.
642 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
643 POINT pt, UINT *pos )
649 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
650 pt.x -= wrect.left;pt.y -= wrect.top;
652 for (i = 0; i < menu->nItems; i++, item++)
654 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
655 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
665 /***********************************************************************
668 * Find the menu item selected by a key press.
669 * Return item id, -1 if none, -2 if we should close the menu.
671 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
672 WCHAR key, BOOL forceMenuChar )
674 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
676 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
680 POPUPMENU *menu = MENU_GetMenu( hmenu );
681 MENUITEM *item = menu->items;
688 for (i = 0; i < menu->nItems; i++, item++)
690 if (IS_STRING_ITEM(item->fType) && item->text)
692 WCHAR *p = item->text - 2;
695 p = strchrW (p + 2, '&');
697 while (p != NULL && p [1] == '&');
698 if (p && (toupperW(p[1]) == toupperW(key))) return i;
702 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
703 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
704 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
705 if (HIWORD(menuchar) == 1) return (UINT)(-2);
711 /***********************************************************************
712 * MENU_GetBitmapItemSize
714 * Get the size of a bitmap item.
716 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
719 HBITMAP bmp = (HBITMAP)id;
721 size->cx = size->cy = 0;
723 /* check if there is a magic menu item associated with this item */
724 if (id && IS_MAGIC_ITEM( id ))
728 case (INT_PTR)HBMMENU_SYSTEM:
735 case (INT_PTR)HBMMENU_MBAR_RESTORE:
736 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
737 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
738 case (INT_PTR)HBMMENU_MBAR_CLOSE:
739 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
740 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
743 case (INT_PTR)HBMMENU_CALLBACK:
744 case (INT_PTR)HBMMENU_POPUP_CLOSE:
745 case (INT_PTR)HBMMENU_POPUP_RESTORE:
746 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
747 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
749 FIXME("Magic 0x%08x not implemented\n", id);
753 if (GetObjectW(bmp, sizeof(bm), &bm ))
755 size->cx = bm.bmWidth;
756 size->cy = bm.bmHeight;
760 /***********************************************************************
761 * MENU_DrawBitmapItem
763 * Draw a bitmap item.
764 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
766 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
771 HBITMAP bmp = (HBITMAP)lpitem->text;
772 int w = rect->right - rect->left;
773 int h = rect->bottom - rect->top;
776 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
778 /* Check if there is a magic menu item associated with this item */
779 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
784 switch(LOWORD(hbmToDraw))
786 case (INT_PTR)HBMMENU_SYSTEM:
787 if (lpitem->dwItemData)
789 bmp = (HBITMAP)lpitem->dwItemData;
790 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
795 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
796 /* only use right half of the bitmap */
797 bmp_xoffset = bm.bmWidth / 2;
798 bm.bmWidth -= bmp_xoffset;
801 case (INT_PTR)HBMMENU_MBAR_RESTORE:
802 flags = DFCS_CAPTIONRESTORE;
804 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
805 flags = DFCS_CAPTIONMIN;
807 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
808 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
810 case (INT_PTR)HBMMENU_MBAR_CLOSE:
811 flags = DFCS_CAPTIONCLOSE;
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
816 case (INT_PTR)HBMMENU_CALLBACK:
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
822 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
826 InflateRect( &r, -1, -1 );
827 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
828 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
832 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
835 hdcMem = CreateCompatibleDC( hdc );
836 SelectObject( hdcMem, bmp );
838 /* handle fontsize > bitmap_height */
839 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
841 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
842 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
843 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
844 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
849 /***********************************************************************
852 * Calculate the size of the menu item and store it in lpitem->rect.
854 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
855 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
858 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
860 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
861 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
862 (menuBar ? " (MenuBar)" : ""));
864 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
866 if (lpitem->fType & MF_OWNERDRAW)
869 ** Experimentation under Windows reveals that an owner-drawn
870 ** menu is expected to return the size of the content part of
871 ** the menu item, not including the checkmark nor the submenu
872 ** arrow. Windows adds those values itself and returns the
873 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
875 MEASUREITEMSTRUCT mis;
876 mis.CtlType = ODT_MENU;
878 mis.itemID = lpitem->wID;
879 mis.itemData = (DWORD)lpitem->dwItemData;
882 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
883 lpitem->rect.right += mis.itemWidth;
887 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
890 /* under at least win95 you seem to be given a standard
891 height for the menu and the height value is ignored */
892 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
895 lpitem->rect.bottom += mis.itemHeight;
897 TRACE("id=%04x size=%dx%d\n",
898 lpitem->wID, mis.itemWidth, mis.itemHeight);
899 /* Fall through to get check/arrow width calculation. */
902 if (lpitem->fType & MF_SEPARATOR)
904 lpitem->rect.bottom += SEPARATOR_HEIGHT;
910 /* New style MIIM_BITMAP */
911 if (lpitem->hbmpItem)
913 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
915 MEASUREITEMSTRUCT measItem;
916 measItem.CtlType = ODT_MENU;
918 measItem.itemID = lpitem->wID;
919 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
920 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
921 measItem.itemData = lpitem->dwItemData;
923 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
925 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
926 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
927 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
928 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
931 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
932 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
933 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
934 lpitem->rect.right += size.cx;
935 lpitem->rect.bottom += size.cy;
937 if (lppop->dwStyle & MNS_CHECKORBMP)
938 lpitem->rect.right += check_bitmap_width;
940 lpitem->rect.right += 2 * check_bitmap_width;
942 lpitem->rect.right += 2 * check_bitmap_width;
943 if (lpitem->fType & MF_POPUP)
944 lpitem->rect.right += arrow_bitmap_width;
947 if (lpitem->fType & MF_OWNERDRAW)
950 if (IS_BITMAP_ITEM(lpitem->fType))
954 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
955 lpitem->rect.right += size.cx;
956 lpitem->rect.bottom += size.cy;
957 /* Leave space for the sunken border */
958 lpitem->rect.right += 2;
959 lpitem->rect.bottom += 2;
963 /* it must be a text item - unless it's the system menu */
964 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
967 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
969 lpitem->rect.right += size.cx;
970 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
975 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
977 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
979 /* Item contains a tab (only meaningful in popup menus) */
980 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
981 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
982 lpitem->rect.right += MENU_TAB_SPACE;
986 if (strchrW( lpitem->text, '\b' ))
987 lpitem->rect.right += MENU_TAB_SPACE;
988 lpitem->xTab = lpitem->rect.right - check_bitmap_width
989 - arrow_bitmap_width;
992 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
996 /***********************************************************************
997 * MENU_PopupMenuCalcSize
999 * Calculate the size of a popup menu.
1001 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1006 int orgX, orgY, maxX, maxTab, maxTabWidth;
1008 lppop->Width = lppop->Height = 0;
1009 if (lppop->nItems == 0) return;
1012 SelectObject( hdc, hMenuFont);
1017 lppop->maxBmpSize.cx = 0;
1018 lppop->maxBmpSize.cy = 0;
1020 while (start < lppop->nItems)
1022 lpitem = &lppop->items[start];
1026 maxTab = maxTabWidth = 0;
1027 /* Parse items until column break or end of menu */
1028 for (i = start; i < lppop->nItems; i++, lpitem++)
1031 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1033 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1035 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1036 maxX = max( maxX, lpitem->rect.right );
1037 orgY = lpitem->rect.bottom;
1038 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1040 maxTab = max( maxTab, lpitem->xTab );
1041 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1045 /* Finish the column (set all items to the largest width found) */
1046 maxX = max( maxX, maxTab + maxTabWidth );
1047 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1049 lpitem->rect.right = maxX;
1050 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1051 lpitem->xTab = maxTab;
1054 lppop->Height = max( lppop->Height, orgY );
1057 lppop->Width = maxX;
1059 /* space for 3d border */
1063 ReleaseDC( 0, hdc );
1067 /***********************************************************************
1068 * MENU_MenuBarCalcSize
1070 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1071 * height is off by 1 pixel which causes lengthy window relocations when
1072 * active document window is maximized/restored.
1074 * Calculate the size of the menu bar.
1076 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1077 LPPOPUPMENU lppop, HWND hwndOwner )
1080 int start, i, orgX, orgY, maxY, helpPos;
1082 if ((lprect == NULL) || (lppop == NULL)) return;
1083 if (lppop->nItems == 0) return;
1084 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1085 lprect->left, lprect->top, lprect->right, lprect->bottom);
1086 lppop->Width = lprect->right - lprect->left;
1088 maxY = lprect->top+1;
1091 lppop->maxBmpSize.cx = 0;
1092 lppop->maxBmpSize.cy = 0;
1093 while (start < lppop->nItems)
1095 lpitem = &lppop->items[start];
1096 orgX = lprect->left;
1099 /* Parse items until line break or end of menu */
1100 for (i = start; i < lppop->nItems; i++, lpitem++)
1102 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1104 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1106 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1108 debug_print_menuitem (" item: ", lpitem, "");
1109 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1111 if (lpitem->rect.right > lprect->right)
1113 if (i != start) break;
1114 else lpitem->rect.right = lprect->right;
1116 maxY = max( maxY, lpitem->rect.bottom );
1117 orgX = lpitem->rect.right;
1120 /* Finish the line (set all items to the largest height found) */
1121 while (start < i) lppop->items[start++].rect.bottom = maxY;
1124 lprect->bottom = maxY;
1125 lppop->Height = lprect->bottom - lprect->top;
1127 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1128 /* the last item (if several lines, only move the last line) */
1129 lpitem = &lppop->items[lppop->nItems-1];
1130 orgY = lpitem->rect.top;
1131 orgX = lprect->right;
1132 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1133 if ( (helpPos==-1) || (helpPos>i) )
1135 if (lpitem->rect.top != orgY) break; /* Other line */
1136 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1137 lpitem->rect.left += orgX - lpitem->rect.right;
1138 lpitem->rect.right = orgX;
1139 orgX = lpitem->rect.left;
1143 /***********************************************************************
1146 * Draw a single menu item.
1148 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1149 UINT height, BOOL menuBar, UINT odaction )
1153 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1155 if (lpitem->fType & MF_SYSMENU)
1157 if( !IsIconic(hwnd) )
1158 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1164 if (lpitem->fState & MF_HILITE)
1167 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1168 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1170 if(lpitem->fState & MF_GRAYED)
1171 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1173 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1174 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1179 if (lpitem->fState & MF_GRAYED)
1180 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1182 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1183 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1186 if (lpitem->fType & MF_OWNERDRAW)
1189 ** Experimentation under Windows reveals that an owner-drawn
1190 ** menu is given the rectangle which includes the space it requested
1191 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1192 ** and a popup-menu arrow. This is the value of lpitem->rect.
1193 ** Windows will leave all drawing to the application except for
1194 ** the popup-menu arrow. Windows always draws that itself, after
1195 ** the menu owner has finished drawing.
1199 dis.CtlType = ODT_MENU;
1201 dis.itemID = lpitem->wID;
1202 dis.itemData = (DWORD)lpitem->dwItemData;
1204 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1205 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1206 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1207 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1208 dis.hwndItem = (HWND)hmenu;
1210 dis.rcItem = lpitem->rect;
1211 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1212 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1213 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1214 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1216 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1217 /* Fall through to draw popup-menu arrow */
1220 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1221 lpitem->rect.right,lpitem->rect.bottom);
1223 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1225 rect = lpitem->rect;
1227 if (!(lpitem->fType & MF_OWNERDRAW))
1229 if (lpitem->fState & MF_HILITE)
1232 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1234 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1237 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1240 SetBkMode( hdc, TRANSPARENT );
1242 if (!(lpitem->fType & MF_OWNERDRAW))
1244 /* vertical separator */
1245 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1249 rc.bottom = height - 3;
1250 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1253 /* horizontal separator */
1254 if (lpitem->fType & MF_SEPARATOR)
1259 rc.top += SEPARATOR_HEIGHT / 2;
1260 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1265 /* helper lines for debugging */
1266 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1267 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1268 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1269 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1275 INT y = rect.top + rect.bottom;
1276 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1277 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1279 if (!(lpitem->fType & MF_OWNERDRAW))
1281 /* New style MIIM_BITMAP */
1282 if (lpitem->hbmpItem)
1284 POPUPMENU *menu = MENU_GetMenu(hmenu);
1285 HBITMAP hbm = lpitem->hbmpItem;
1287 if (hbm == HBMMENU_CALLBACK)
1289 DRAWITEMSTRUCT drawItem;
1290 drawItem.CtlType = ODT_MENU;
1292 drawItem.itemID = lpitem->wID;
1293 drawItem.itemAction = odaction;
1294 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1295 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1296 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1297 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1298 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1299 drawItem.hwndItem = (HWND)hmenu;
1301 drawItem.rcItem = lpitem->rect;
1302 drawItem.itemData = lpitem->dwItemData;
1304 if (!(lpitem->fState & MF_CHECKED))
1305 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1308 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1310 if (menu->dwStyle & MNS_CHECKORBMP)
1311 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1313 rect.left += menu->maxBmpSize.cx;
1315 /* Draw the check mark
1318 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1320 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1321 if (bm) /* we have a custom bitmap */
1323 HDC hdcMem = CreateCompatibleDC( hdc );
1324 SelectObject( hdcMem, bm );
1325 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1326 check_bitmap_width, check_bitmap_height,
1327 hdcMem, 0, 0, SRCCOPY );
1330 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1333 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1334 HDC hdcMem = CreateCompatibleDC( hdc );
1335 SelectObject( hdcMem, bm );
1336 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1337 DrawFrameControl( hdcMem, &r, DFC_MENU,
1338 (lpitem->fType & MFT_RADIOCHECK) ?
1339 DFCS_MENUBULLET : DFCS_MENUCHECK );
1340 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1341 hdcMem, 0, 0, SRCCOPY );
1347 /* Draw the popup-menu arrow */
1348 if (lpitem->fType & MF_POPUP)
1350 HDC hdcMem = CreateCompatibleDC( hdc );
1351 HBITMAP hOrigBitmap;
1353 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1354 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1355 (y - arrow_bitmap_height) / 2,
1356 arrow_bitmap_width, arrow_bitmap_height,
1357 hdcMem, 0, 0, SRCCOPY );
1358 SelectObject( hdcMem, hOrigBitmap );
1362 rect.left += check_bitmap_width;
1363 rect.right -= arrow_bitmap_width;
1366 /* Done for owner-drawn */
1367 if (lpitem->fType & MF_OWNERDRAW)
1370 /* Draw the item text or bitmap */
1371 if (IS_BITMAP_ITEM(lpitem->fType))
1373 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1376 /* No bitmap - process text if present */
1377 else if (IS_STRING_ITEM(lpitem->fType))
1382 UINT uFormat = (menuBar) ?
1383 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1384 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1386 if ( lpitem->fState & MFS_DEFAULT )
1388 hfontOld = SelectObject( hdc, hMenuFontBold);
1393 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1394 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1397 for (i = 0; lpitem->text[i]; i++)
1398 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1401 if(lpitem->fState & MF_GRAYED)
1403 if (!(lpitem->fState & MF_HILITE) )
1405 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1406 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1407 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1408 --rect.left; --rect.top; --rect.right; --rect.bottom;
1410 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1413 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1415 /* paint the shortcut text */
1416 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1418 if (lpitem->text[i] == '\t')
1420 rect.left = lpitem->xTab;
1421 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1425 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1428 if(lpitem->fState & MF_GRAYED)
1430 if (!(lpitem->fState & MF_HILITE) )
1432 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1433 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1434 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1435 --rect.left; --rect.top; --rect.right; --rect.bottom;
1437 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1439 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1443 SelectObject (hdc, hfontOld);
1448 /***********************************************************************
1449 * MENU_DrawPopupMenu
1451 * Paint a popup menu.
1453 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1455 HBRUSH hPrevBrush = 0;
1458 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1460 GetClientRect( hwnd, &rect );
1462 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1463 && (SelectObject( hdc, hMenuFont)))
1467 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1469 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1474 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1476 /* draw menu items */
1478 menu = MENU_GetMenu( hmenu );
1479 if (menu && menu->nItems)
1484 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1485 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1486 menu->Height, FALSE, ODA_DRAWENTIRE );
1491 SelectObject( hdc, hPrevBrush );
1496 /***********************************************************************
1499 * Paint a menu bar. Returns the height of the menu bar.
1500 * called from [windows/nonclient.c]
1502 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1507 HMENU hMenu = GetMenu(hwnd);
1509 lppop = MENU_GetMenu( hMenu );
1510 if (lppop == NULL || lprect == NULL)
1512 return GetSystemMetrics(SM_CYMENU);
1517 hfontOld = SelectObject( hDC, hMenuFont);
1519 if (lppop->Height == 0)
1520 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1522 lprect->bottom = lprect->top + lppop->Height;
1524 if (hfontOld) SelectObject( hDC, hfontOld);
1525 return lppop->Height;
1528 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1532 /***********************************************************************
1535 * Display a popup menu.
1537 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1538 INT x, INT y, INT xanchor, INT yanchor )
1543 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1544 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1546 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1547 if (menu->FocusedItem != NO_SELECTED_ITEM)
1549 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1550 menu->FocusedItem = NO_SELECTED_ITEM;
1553 /* store the owner for DrawItem */
1554 menu->hwndOwner = hwndOwner;
1556 MENU_PopupMenuCalcSize( menu, hwndOwner );
1558 /* adjust popup menu pos so that it fits within the desktop */
1560 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1561 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1563 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1566 x -= width - xanchor;
1567 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1568 x = GetSystemMetrics(SM_CXSCREEN) - width;
1572 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1575 y -= height + yanchor;
1576 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1577 y = GetSystemMetrics(SM_CYSCREEN) - height;
1581 /* NOTE: In Windows, top menu popup is not owned. */
1582 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1583 WS_POPUP, x, y, width, height,
1584 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1586 if( !menu->hWnd ) return FALSE;
1587 if (!top_popup) top_popup = menu->hWnd;
1589 /* Display the window */
1591 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1592 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1593 UpdateWindow( menu->hWnd );
1598 /***********************************************************************
1601 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1602 BOOL sendMenuSelect, HMENU topmenu )
1607 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1609 lppop = MENU_GetMenu( hmenu );
1610 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1612 if (lppop->FocusedItem == wIndex) return;
1613 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1614 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1615 if (!top_popup) top_popup = lppop->hWnd;
1617 SelectObject( hdc, hMenuFont);
1619 /* Clear previous highlighted item */
1620 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1622 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1623 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1624 lppop->Height, !(lppop->wFlags & MF_POPUP),
1628 /* Highlight new item (if any) */
1629 lppop->FocusedItem = wIndex;
1630 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1632 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1633 lppop->items[wIndex].fState |= MF_HILITE;
1634 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1635 &lppop->items[wIndex], lppop->Height,
1636 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1640 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1641 SendMessageW( hwndOwner, WM_MENUSELECT,
1642 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1643 ip->fType | ip->fState |
1644 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1647 else if (sendMenuSelect) {
1650 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1651 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1652 MENUITEM *ip = &ptm->items[pos];
1653 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1654 ip->fType | ip->fState |
1655 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1659 ReleaseDC( lppop->hWnd, hdc );
1663 /***********************************************************************
1664 * MENU_MoveSelection
1666 * Moves currently selected item according to the offset parameter.
1667 * If there is no selection then it should select the last item if
1668 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1670 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1675 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1677 menu = MENU_GetMenu( hmenu );
1678 if ((!menu) || (!menu->items)) return;
1680 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1682 if( menu->nItems == 1 ) return; else
1683 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1685 if (!(menu->items[i].fType & MF_SEPARATOR))
1687 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1692 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1693 i >= 0 && i < menu->nItems ; i += offset)
1694 if (!(menu->items[i].fType & MF_SEPARATOR))
1696 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1702 /**********************************************************************
1705 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1708 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1711 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1713 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1714 TRACE("flags=%x str=%p\n", flags, str);
1716 if (IS_STRING_ITEM(flags))
1720 flags |= MF_SEPARATOR;
1726 /* Item beginning with a backspace is a help item */
1732 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1734 strcpyW( text, str );
1738 else if (IS_BITMAP_ITEM(flags))
1739 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1740 else item->text = NULL;
1742 if (flags & MF_OWNERDRAW)
1743 item->dwItemData = (DWORD)str;
1745 item->dwItemData = 0;
1747 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1748 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1750 if (flags & MF_POPUP)
1752 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1753 if (menu) menu->wFlags |= MF_POPUP;
1765 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1767 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1768 flags |= MF_POPUP; /* keep popup */
1770 item->fType = flags & TYPE_MASK;
1771 item->fState = (flags & STATE_MASK) &
1772 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1775 /* Don't call SetRectEmpty here! */
1778 HeapFree( GetProcessHeap(), 0, prevText );
1780 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1785 /**********************************************************************
1788 * Insert (allocate) a new item into a menu.
1790 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1795 if (!(menu = MENU_GetMenu(hMenu)))
1798 /* Find where to insert new item */
1800 if (flags & MF_BYPOSITION) {
1801 if (pos > menu->nItems)
1804 if (!MENU_FindItem( &hMenu, &pos, flags ))
1807 if (!(menu = MENU_GetMenu( hMenu )))
1812 /* Create new items array */
1814 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1817 WARN("allocation failed\n" );
1820 if (menu->nItems > 0)
1822 /* Copy the old array into the new one */
1823 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1824 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1825 (menu->nItems-pos)*sizeof(MENUITEM) );
1826 HeapFree( GetProcessHeap(), 0, menu->items );
1828 menu->items = newItems;
1830 memset( &newItems[pos], 0, sizeof(*newItems) );
1831 menu->Height = 0; /* force size recalculate */
1832 return &newItems[pos];
1836 /**********************************************************************
1837 * MENU_ParseResource
1839 * Parse a standard menu resource and add items to the menu.
1840 * Return a pointer to the end of the resource.
1842 * NOTE: flags is equivalent to the mtOption field
1844 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1851 flags = GET_WORD(res);
1852 res += sizeof(WORD);
1853 if (!(flags & MF_POPUP))
1856 res += sizeof(WORD);
1859 if (!unicode) res += strlen(str) + 1;
1860 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1861 if (flags & MF_POPUP)
1863 HMENU hSubMenu = CreatePopupMenu();
1864 if (!hSubMenu) return NULL;
1865 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1867 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1868 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1870 else /* Not a popup */
1872 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1873 else AppendMenuW( hMenu, flags, id,
1874 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1876 } while (!(flags & MF_END));
1881 /**********************************************************************
1882 * MENUEX_ParseResource
1884 * Parse an extended menu resource and add items to the menu.
1885 * Return a pointer to the end of the resource.
1887 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1893 mii.cbSize = sizeof(mii);
1894 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1895 mii.fType = GET_DWORD(res);
1896 res += sizeof(DWORD);
1897 mii.fState = GET_DWORD(res);
1898 res += sizeof(DWORD);
1899 mii.wID = GET_DWORD(res);
1900 res += sizeof(DWORD);
1901 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1902 res += sizeof(WORD);
1903 /* Align the text on a word boundary. */
1904 res += (~((int)res - 1)) & 1;
1905 mii.dwTypeData = (LPWSTR) res;
1906 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1907 /* Align the following fields on a dword boundary. */
1908 res += (~((int)res - 1)) & 3;
1910 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1911 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1913 if (resinfo & 1) { /* Pop-up? */
1914 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1915 res += sizeof(DWORD);
1916 mii.hSubMenu = CreatePopupMenu();
1919 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1920 DestroyMenu(mii.hSubMenu);
1923 mii.fMask |= MIIM_SUBMENU;
1924 mii.fType |= MF_POPUP;
1926 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1928 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1929 mii.wID, mii.fType);
1930 mii.fType |= MF_SEPARATOR;
1932 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1933 } while (!(resinfo & MF_END));
1938 /***********************************************************************
1941 * Return the handle of the selected sub-popup menu (if any).
1943 static HMENU MENU_GetSubPopup( HMENU hmenu )
1948 menu = MENU_GetMenu( hmenu );
1950 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1952 item = &menu->items[menu->FocusedItem];
1953 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1954 return item->hSubMenu;
1959 /***********************************************************************
1960 * MENU_HideSubPopups
1962 * Hide the sub-popup menus of this menu.
1964 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1965 BOOL sendMenuSelect )
1967 POPUPMENU *menu = MENU_GetMenu( hmenu );
1969 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1971 if (menu && top_popup)
1977 if (menu->FocusedItem != NO_SELECTED_ITEM)
1979 item = &menu->items[menu->FocusedItem];
1980 if (!(item->fType & MF_POPUP) ||
1981 !(item->fState & MF_MOUSESELECT)) return;
1982 item->fState &= ~MF_MOUSESELECT;
1983 hsubmenu = item->hSubMenu;
1986 submenu = MENU_GetMenu( hsubmenu );
1987 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1988 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1989 DestroyWindow( submenu->hWnd );
1995 /***********************************************************************
1998 * Display the sub-menu of the selected item of this menu.
1999 * Return the handle of the submenu, or hmenu if no submenu to display.
2001 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2002 BOOL selectFirst, UINT wFlags )
2009 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2011 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2013 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2015 item = &menu->items[menu->FocusedItem];
2016 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2019 /* message must be sent before using item,
2020 because nearly everything may be changed by the application ! */
2022 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2023 if (!(wFlags & TPM_NONOTIFY))
2024 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2025 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2027 item = &menu->items[menu->FocusedItem];
2030 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2031 if (!(item->fState & MF_HILITE))
2033 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2034 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2036 SelectObject( hdc, hMenuFont);
2038 item->fState |= MF_HILITE;
2039 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2040 ReleaseDC( menu->hWnd, hdc );
2042 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2045 item->fState |= MF_MOUSESELECT;
2047 if (IS_SYSTEM_MENU(menu))
2049 MENU_InitSysMenuPopup(item->hSubMenu,
2050 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2051 GetClassLongW( menu->hWnd, GCL_STYLE));
2053 NC_GetSysPopupPos( menu->hWnd, &rect );
2054 rect.top = rect.bottom;
2055 rect.right = GetSystemMetrics(SM_CXSIZE);
2056 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2060 GetWindowRect( menu->hWnd, &rect );
2061 if (menu->wFlags & MF_POPUP)
2063 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2064 rect.top += item->rect.top;
2065 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2066 rect.bottom = item->rect.top - item->rect.bottom;
2070 rect.left += item->rect.left;
2071 rect.top += item->rect.bottom;
2072 rect.right = item->rect.right - item->rect.left;
2073 rect.bottom = item->rect.bottom - item->rect.top;
2077 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2078 rect.left, rect.top, rect.right, rect.bottom );
2080 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2081 return item->hSubMenu;
2086 /**********************************************************************
2089 HWND MENU_IsMenuActive(void)
2094 /***********************************************************************
2097 * Walks menu chain trying to find a menu pt maps to.
2099 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2101 POPUPMENU *menu = MENU_GetMenu( hMenu );
2102 UINT item = menu->FocusedItem;
2105 /* try subpopup first (if any) */
2106 ret = (item != NO_SELECTED_ITEM &&
2107 (menu->items[item].fType & MF_POPUP) &&
2108 (menu->items[item].fState & MF_MOUSESELECT))
2109 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2111 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2113 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2114 if( menu->wFlags & MF_POPUP )
2116 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2118 else if (ht == HTSYSMENU)
2119 ret = get_win_sys_menu( menu->hWnd );
2120 else if (ht == HTMENU)
2121 ret = GetMenu( menu->hWnd );
2126 /***********************************************************************
2127 * MENU_ExecFocusedItem
2129 * Execute a menu item (for instance when user pressed Enter).
2130 * Return the wID of the executed item. Otherwise, -1 indicating
2131 * that no menu item was executed;
2132 * Have to receive the flags for the TrackPopupMenu options to avoid
2133 * sending unwanted message.
2136 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2139 POPUPMENU *menu = MENU_GetMenu( hMenu );
2141 TRACE("%p hmenu=%p\n", pmt, hMenu);
2143 if (!menu || !menu->nItems ||
2144 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2146 item = &menu->items[menu->FocusedItem];
2148 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2150 if (!(item->fType & MF_POPUP))
2152 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2154 /* If TPM_RETURNCMD is set you return the id, but
2155 do not send a message to the owner */
2156 if(!(wFlags & TPM_RETURNCMD))
2158 if( menu->wFlags & MF_SYSMENU )
2159 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2160 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2162 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2168 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2173 /***********************************************************************
2174 * MENU_SwitchTracking
2176 * Helper function for menu navigation routines.
2178 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2180 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2181 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2183 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2185 if( pmt->hTopMenu != hPtMenu &&
2186 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2188 /* both are top level menus (system and menu-bar) */
2189 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2190 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2191 pmt->hTopMenu = hPtMenu;
2193 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2194 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2198 /***********************************************************************
2201 * Return TRUE if we can go on with menu tracking.
2203 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2205 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2210 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2213 if( IS_SYSTEM_MENU(ptmenu) )
2214 item = ptmenu->items;
2216 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2220 if( ptmenu->FocusedItem != id )
2221 MENU_SwitchTracking( pmt, hPtMenu, id );
2223 /* If the popup menu is not already "popped" */
2224 if(!(item->fState & MF_MOUSESELECT ))
2226 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2231 /* Else the click was on the menu bar, finish the tracking */
2236 /***********************************************************************
2239 * Return the value of MENU_ExecFocusedItem if
2240 * the selected item was not a popup. Else open the popup.
2241 * A -1 return value indicates that we go on with menu tracking.
2244 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2246 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2251 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2254 if( IS_SYSTEM_MENU(ptmenu) )
2255 item = ptmenu->items;
2257 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2259 if( item && (ptmenu->FocusedItem == id ))
2261 if( !(item->fType & MF_POPUP) )
2262 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2264 /* If we are dealing with the top-level menu */
2265 /* and this is a click on an already "popped" item: */
2266 /* Stop the menu tracking and close the opened submenus */
2267 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2270 ptmenu->bTimeToHide = TRUE;
2276 /***********************************************************************
2279 * Return TRUE if we can go on with menu tracking.
2281 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2283 UINT id = NO_SELECTED_ITEM;
2284 POPUPMENU *ptmenu = NULL;
2288 ptmenu = MENU_GetMenu( hPtMenu );
2289 if( IS_SYSTEM_MENU(ptmenu) )
2292 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2295 if( id == NO_SELECTED_ITEM )
2297 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2298 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2301 else if( ptmenu->FocusedItem != id )
2303 MENU_SwitchTracking( pmt, hPtMenu, id );
2304 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2310 /***********************************************************************
2313 static void MENU_SetCapture( HWND hwnd )
2317 SERVER_START_REQ( set_capture_window )
2320 req->flags = CAPTURE_MENU;
2321 if (!wine_server_call_err( req ))
2323 previous = reply->previous;
2324 hwnd = reply->full_handle;
2329 if (previous && previous != hwnd)
2330 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2334 /***********************************************************************
2337 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2339 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2341 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2343 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2344 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2346 MDINEXTMENU next_menu;
2351 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2352 next_menu.hmenuNext = 0;
2353 next_menu.hwndNext = 0;
2354 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2356 TRACE("%p [%p] -> %p [%p]\n",
2357 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2359 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2361 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2362 hNewWnd = pmt->hOwnerWnd;
2363 if( IS_SYSTEM_MENU(menu) )
2365 /* switch to the menu bar */
2367 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2371 menu = MENU_GetMenu( hNewMenu );
2372 id = menu->nItems - 1;
2375 else if (style & WS_SYSMENU )
2377 /* switch to the system menu */
2378 hNewMenu = get_win_sys_menu( hNewWnd );
2382 else /* application returned a new menu to switch to */
2384 hNewMenu = next_menu.hmenuNext;
2385 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2387 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2389 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2391 if (style & WS_SYSMENU &&
2392 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2394 /* get the real system menu */
2395 hNewMenu = get_win_sys_menu(hNewWnd);
2397 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2399 /* FIXME: Not sure what to do here;
2400 * perhaps try to track hNewMenu as a popup? */
2402 TRACE(" -- got confused.\n");
2409 if( hNewMenu != pmt->hTopMenu )
2411 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2413 if( pmt->hCurrentMenu != pmt->hTopMenu )
2414 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2417 if( hNewWnd != pmt->hOwnerWnd )
2419 pmt->hOwnerWnd = hNewWnd;
2420 MENU_SetCapture( pmt->hOwnerWnd );
2423 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2424 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2431 /***********************************************************************
2434 * The idea is not to show the popup if the next input message is
2435 * going to hide it anyway.
2437 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2441 msg.hwnd = pmt->hOwnerWnd;
2443 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2444 pmt->trackFlags |= TF_SKIPREMOVE;
2449 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2450 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2452 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2453 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2454 if( msg.message == WM_KEYDOWN &&
2455 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2457 pmt->trackFlags |= TF_SUSPENDPOPUP;
2464 /* failures go through this */
2465 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2469 /***********************************************************************
2472 * Handle a VK_ESCAPE key event in a menu.
2474 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2476 BOOL bEndMenu = TRUE;
2478 if (pmt->hCurrentMenu != pmt->hTopMenu)
2480 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2482 if (menu->wFlags & MF_POPUP)
2484 HMENU hmenutmp, hmenuprev;
2486 hmenuprev = hmenutmp = pmt->hTopMenu;
2488 /* close topmost popup */
2489 while (hmenutmp != pmt->hCurrentMenu)
2491 hmenuprev = hmenutmp;
2492 hmenutmp = MENU_GetSubPopup( hmenuprev );
2495 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2496 pmt->hCurrentMenu = hmenuprev;
2504 /***********************************************************************
2507 * Handle a VK_LEFT key event in a menu.
2509 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2512 HMENU hmenutmp, hmenuprev;
2515 hmenuprev = hmenutmp = pmt->hTopMenu;
2516 menu = MENU_GetMenu( hmenutmp );
2518 /* Try to move 1 column left (if possible) */
2519 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2520 NO_SELECTED_ITEM ) {
2522 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2527 /* close topmost popup */
2528 while (hmenutmp != pmt->hCurrentMenu)
2530 hmenuprev = hmenutmp;
2531 hmenutmp = MENU_GetSubPopup( hmenuprev );
2534 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2535 pmt->hCurrentMenu = hmenuprev;
2537 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2539 /* move menu bar selection if no more popups are left */
2541 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2542 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2544 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2546 /* A sublevel menu was displayed - display the next one
2547 * unless there is another displacement coming up */
2549 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2550 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2551 pmt->hTopMenu, TRUE, wFlags);
2557 /***********************************************************************
2560 * Handle a VK_RIGHT key event in a menu.
2562 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2565 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2568 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2570 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2571 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2573 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2575 /* If already displaying a popup, try to display sub-popup */
2577 hmenutmp = pmt->hCurrentMenu;
2578 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2580 /* if subpopup was displayed then we are done */
2581 if (hmenutmp != pmt->hCurrentMenu) return;
2584 /* Check to see if there's another column */
2585 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2586 NO_SELECTED_ITEM ) {
2587 TRACE("Going to %d.\n", nextcol );
2588 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2593 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2595 if( pmt->hCurrentMenu != pmt->hTopMenu )
2597 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2598 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2599 } else hmenutmp = 0;
2601 /* try to move to the next item */
2602 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2603 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2605 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2606 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2607 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2608 pmt->hTopMenu, TRUE, wFlags);
2612 /***********************************************************************
2615 * Menu tracking code.
2617 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2618 HWND hwnd, const RECT *lprect )
2623 INT executedMenuId = -1;
2625 BOOL enterIdleSent = FALSE;
2628 mt.hCurrentMenu = hmenu;
2629 mt.hTopMenu = hmenu;
2630 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2634 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2635 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2636 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2639 if (!(menu = MENU_GetMenu( hmenu )))
2641 WARN("Invalid menu handle %p\n", hmenu);
2642 SetLastError(ERROR_INVALID_MENU_HANDLE);
2646 if (wFlags & TPM_BUTTONDOWN)
2648 /* Get the result in order to start the tracking or not */
2649 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2650 fEndMenu = !fRemove;
2653 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2655 MENU_SetCapture( mt.hOwnerWnd );
2659 menu = MENU_GetMenu( mt.hCurrentMenu );
2660 if (!menu) /* sometimes happens if I do a window manager close */
2663 /* we have to keep the message in the queue until it's
2664 * clear that menu loop is not over yet. */
2668 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2670 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2671 /* remove the message from the queue */
2672 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2678 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2679 enterIdleSent = TRUE;
2680 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2686 /* check if EndMenu() tried to cancel us, by posting this message */
2687 if(msg.message == WM_CANCELMODE)
2689 /* we are now out of the loop */
2692 /* remove the message from the queue */
2693 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2695 /* break out of internal loop, ala ESCAPE */
2699 TranslateMessage( &msg );
2702 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2703 enterIdleSent=FALSE;
2706 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2709 * Use the mouse coordinates in lParam instead of those in the MSG
2710 * struct to properly handle synthetic messages. They are already
2711 * in screen coordinates.
2713 mt.pt.x = (short)LOWORD(msg.lParam);
2714 mt.pt.y = (short)HIWORD(msg.lParam);
2716 /* Find a menu for this mouse event */
2717 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2721 /* no WM_NC... messages in captured state */
2723 case WM_RBUTTONDBLCLK:
2724 case WM_RBUTTONDOWN:
2725 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2727 case WM_LBUTTONDBLCLK:
2728 case WM_LBUTTONDOWN:
2729 /* If the message belongs to the menu, removes it from the queue */
2730 /* Else, end menu tracking */
2731 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2732 fEndMenu = !fRemove;
2736 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2739 /* Check if a menu was selected by the mouse */
2742 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2744 /* End the loop if executedMenuId is an item ID */
2745 /* or if the job was done (executedMenuId = 0). */
2746 fEndMenu = fRemove = (executedMenuId != -1);
2748 /* No menu was selected by the mouse */
2749 /* if the function was called by TrackPopupMenu, continue
2750 with the menu tracking. If not, stop it */
2752 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2757 /* the selected menu item must be changed every time */
2758 /* the mouse moves. */
2761 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2763 } /* switch(msg.message) - mouse */
2765 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2767 fRemove = TRUE; /* Keyboard messages are always removed */
2780 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2781 NO_SELECTED_ITEM, FALSE, 0 );
2784 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2785 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2788 case VK_DOWN: /* If on menu bar, pull-down the menu */
2790 menu = MENU_GetMenu( mt.hCurrentMenu );
2791 if (!(menu->wFlags & MF_POPUP))
2792 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2793 else /* otherwise try to move selection */
2794 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2798 MENU_KeyLeft( &mt, wFlags );
2802 MENU_KeyRight( &mt, wFlags );
2806 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2812 hi.cbSize = sizeof(HELPINFO);
2813 hi.iContextType = HELPINFO_MENUITEM;
2814 if (menu->FocusedItem == NO_SELECTED_ITEM)
2817 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2818 hi.hItemHandle = hmenu;
2819 hi.dwContextId = menu->dwContextHelpID;
2820 hi.MousePos = msg.pt;
2821 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2828 break; /* WM_KEYDOWN */
2835 if (msg.wParam == '\r' || msg.wParam == ' ')
2837 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2838 fEndMenu = (executedMenuId != -1);
2843 /* Hack to avoid control chars. */
2844 /* We will find a better way real soon... */
2845 if (msg.wParam < 32) break;
2847 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2848 LOWORD(msg.wParam), FALSE );
2849 if (pos == (UINT)-2) fEndMenu = TRUE;
2850 else if (pos == (UINT)-1) MessageBeep(0);
2853 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2855 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2856 fEndMenu = (executedMenuId != -1);
2860 } /* switch(msg.message) - kbd */
2864 DispatchMessageW( &msg );
2867 if (!fEndMenu) fRemove = TRUE;
2869 /* finally remove message from the queue */
2871 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2872 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2873 else mt.trackFlags &= ~TF_SKIPREMOVE;
2876 MENU_SetCapture(0); /* release the capture */
2878 /* If dropdown is still painted and the close box is clicked on
2879 then the menu will be destroyed as part of the DispatchMessage above.
2880 This will then invalidate the menu handle in mt.hTopMenu. We should
2881 check for this first. */
2882 if( IsMenu( mt.hTopMenu ) )
2884 menu = MENU_GetMenu( mt.hTopMenu );
2886 if( IsWindow( mt.hOwnerWnd ) )
2888 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2890 if (menu && (menu->wFlags & MF_POPUP))
2892 DestroyWindow( menu->hWnd );
2895 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2896 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2899 /* Reset the variable for hiding menu */
2900 if( menu ) menu->bTimeToHide = FALSE;
2903 /* The return value is only used by TrackPopupMenu */
2904 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2905 if (executedMenuId == -1) executedMenuId = 0;
2906 return executedMenuId;
2909 /***********************************************************************
2912 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2916 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2920 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2921 if (!(wFlags & TPM_NONOTIFY))
2922 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2924 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2926 if (!(wFlags & TPM_NONOTIFY))
2928 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2929 /* If an app changed/recreated menu bar entries in WM_INITMENU
2930 * menu sizes will be recalculated once the menu created/shown.
2934 /* This makes the menus of applications built with Delphi work.
2935 * It also enables menus to be displayed in more than one window,
2936 * but there are some bugs left that need to be fixed in this case.
2938 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2942 /***********************************************************************
2945 static BOOL MENU_ExitTracking(HWND hWnd)
2947 TRACE("hwnd=%p\n", hWnd);
2949 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2955 /***********************************************************************
2956 * MENU_TrackMouseMenuBar
2958 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2960 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2962 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2963 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2965 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2969 /* map point to parent client coordinates */
2970 HWND parent = GetAncestor( hWnd, GA_PARENT );
2971 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2973 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2974 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2975 MENU_ExitTracking(hWnd);
2980 /***********************************************************************
2981 * MENU_TrackKbdMenuBar
2983 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2985 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2987 UINT uItem = NO_SELECTED_ITEM;
2989 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2991 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2993 /* find window that has a menu */
2995 while (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)
2996 if (!(hwnd = GetParent( hwnd ))) return;
2998 /* check if we have to track a system menu */
3000 hTrackMenu = GetMenu( hwnd );
3001 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3003 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3004 hTrackMenu = get_win_sys_menu( hwnd );
3006 wParam |= HTSYSMENU; /* prevent item lookup */
3009 if (!IsMenu( hTrackMenu )) return;
3011 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3013 if( wChar && wChar != ' ' )
3015 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3016 if ( uItem >= (UINT)(-2) )
3018 if( uItem == (UINT)(-1) ) MessageBeep(0);
3019 /* schedule end of menu tracking */
3020 wFlags |= TF_ENDMENU;
3025 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3027 if (wParam & HTSYSMENU)
3029 /* prevent sysmenu activation for managed windows on Alt down/up */
3030 if (GetPropA( hwnd, "__wine_x11_managed" ))
3031 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3035 if( uItem == NO_SELECTED_ITEM )
3036 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3038 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3042 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3043 MENU_ExitTracking( hwnd );
3047 /**********************************************************************
3048 * TrackPopupMenu (USER32.@)
3050 * Like the win32 API, the function return the command ID only if the
3051 * flag TPM_RETURNCMD is on.
3054 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3055 INT nReserved, HWND hWnd, const RECT *lpRect )
3059 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3061 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3062 if (!(wFlags & TPM_NONOTIFY))
3063 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3065 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3066 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3067 MENU_ExitTracking(hWnd);
3072 /**********************************************************************
3073 * TrackPopupMenuEx (USER32.@)
3075 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3076 HWND hWnd, LPTPMPARAMS lpTpm )
3078 FIXME("not fully implemented\n" );
3079 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3080 lpTpm ? &lpTpm->rcExclude : NULL );
3083 /***********************************************************************
3086 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3088 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3090 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3096 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3097 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3101 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3102 return MA_NOACTIVATE;
3107 BeginPaint( hwnd, &ps );
3108 MENU_DrawPopupMenu( hwnd, ps.hdc,
3109 (HMENU)GetWindowLongW( hwnd, 0 ) );
3110 EndPaint( hwnd, &ps );
3117 /* zero out global pointer in case resident popup window was destroyed. */
3118 if (hwnd == top_popup) top_popup = 0;
3125 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3128 SetWindowLongW( hwnd, 0, 0 );
3131 case MM_SETMENUHANDLE:
3132 SetWindowLongW( hwnd, 0, wParam );
3135 case MM_GETMENUHANDLE:
3136 return GetWindowLongW( hwnd, 0 );
3139 return DefWindowProcW( hwnd, message, wParam, lParam );
3145 /***********************************************************************
3146 * MENU_GetMenuBarHeight
3148 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3150 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3151 INT orgX, INT orgY )
3157 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3159 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3161 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3162 SelectObject( hdc, hMenuFont);
3163 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3164 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3165 ReleaseDC( hwnd, hdc );
3166 return lppop->Height;
3170 /*******************************************************************
3171 * ChangeMenuA (USER32.@)
3173 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3174 UINT id, UINT flags )
3176 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3177 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3179 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3180 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3182 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3183 flags & MF_BYPOSITION ? pos : id,
3184 flags & ~MF_REMOVE );
3185 /* Default: MF_INSERT */
3186 return InsertMenuA( hMenu, pos, flags, id, data );
3190 /*******************************************************************
3191 * ChangeMenuW (USER32.@)
3193 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3194 UINT id, UINT flags )
3196 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3197 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3199 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3200 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3202 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3203 flags & MF_BYPOSITION ? pos : id,
3204 flags & ~MF_REMOVE );
3205 /* Default: MF_INSERT */
3206 return InsertMenuW( hMenu, pos, flags, id, data );
3210 /*******************************************************************
3211 * CheckMenuItem (USER32.@)
3213 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3218 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3219 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3220 ret = item->fState & MF_CHECKED;
3221 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3222 else item->fState &= ~MF_CHECKED;
3227 /**********************************************************************
3228 * EnableMenuItem (USER32.@)
3230 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3236 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3238 /* Get the Popupmenu to access the owner menu */
3239 if (!(menu = MENU_GetMenu(hMenu)))
3242 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3245 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3246 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3248 /* If the close item in the system menu change update the close button */
3249 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3251 if (menu->hSysMenuOwner != 0)
3253 POPUPMENU* parentMenu;
3255 /* Get the parent menu to access*/
3256 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3259 /* Refresh the frame to reflect the change*/
3260 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3261 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3269 /*******************************************************************
3270 * GetMenuStringA (USER32.@)
3272 INT WINAPI GetMenuStringA(
3273 HMENU hMenu, /* [in] menuhandle */
3274 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3275 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3276 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3277 UINT wFlags /* [in] MF_ flags */
3281 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3282 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3283 if (!IS_STRING_ITEM(item->fType)) return 0;
3284 if (!str || !nMaxSiz) return strlenW(item->text);
3286 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3288 TRACE("returning '%s'\n", str );
3293 /*******************************************************************
3294 * GetMenuStringW (USER32.@)
3296 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3297 LPWSTR str, INT nMaxSiz, UINT wFlags )
3301 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3302 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3303 if (!IS_STRING_ITEM(item->fType)) return 0;
3304 if (!str || !nMaxSiz) return strlenW(item->text);
3306 lstrcpynW( str, item->text, nMaxSiz );
3307 return strlenW(str);
3311 /**********************************************************************
3312 * HiliteMenuItem (USER32.@)
3314 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3318 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3319 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3320 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3321 if (menu->FocusedItem == wItemID) return TRUE;
3322 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3323 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3328 /**********************************************************************
3329 * GetMenuState (USER32.@)
3331 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3334 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3335 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3336 debug_print_menuitem (" item: ", item, "");
3337 if (item->fType & MF_POPUP)
3339 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3340 if (!menu) return -1;
3341 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3345 /* We used to (from way back then) mask the result to 0xff. */
3346 /* I don't know why and it seems wrong as the documented */
3347 /* return flag MF_SEPARATOR is outside that mask. */
3348 return (item->fType | item->fState);
3353 /**********************************************************************
3354 * GetMenuItemCount (USER32.@)
3356 INT WINAPI GetMenuItemCount( HMENU hMenu )
3358 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3359 if (!menu) return -1;
3360 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3361 return menu->nItems;
3365 /**********************************************************************
3366 * GetMenuItemID (USER32.@)
3368 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3372 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3373 if (lpmi->fType & MF_POPUP) return -1;
3379 /*******************************************************************
3380 * InsertMenuW (USER32.@)
3382 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3383 UINT_PTR id, LPCWSTR str )
3387 if (IS_STRING_ITEM(flags) && str)
3388 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3389 hMenu, pos, flags, id, debugstr_w(str) );
3390 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3391 hMenu, pos, flags, id, (DWORD)str );
3393 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3395 if (!(MENU_SetItemData( item, flags, id, str )))
3397 RemoveMenu( hMenu, pos, flags );
3401 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3402 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3404 item->hCheckBit = item->hUnCheckBit = 0;
3409 /*******************************************************************
3410 * InsertMenuA (USER32.@)
3412 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3413 UINT_PTR id, LPCSTR str )
3417 if (IS_STRING_ITEM(flags) && str)
3419 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3420 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3423 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3424 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3425 HeapFree( GetProcessHeap(), 0, newstr );
3429 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3433 /*******************************************************************
3434 * AppendMenuA (USER32.@)
3436 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3437 UINT_PTR id, LPCSTR data )
3439 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3443 /*******************************************************************
3444 * AppendMenuW (USER32.@)
3446 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3447 UINT_PTR id, LPCWSTR data )
3449 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3453 /**********************************************************************
3454 * RemoveMenu (USER32.@)
3456 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3461 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3462 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3463 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3467 MENU_FreeItemData( item );
3469 if (--menu->nItems == 0)
3471 HeapFree( GetProcessHeap(), 0, menu->items );
3476 while(nPos < menu->nItems)
3482 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3483 menu->nItems * sizeof(MENUITEM) );
3489 /**********************************************************************
3490 * DeleteMenu (USER32.@)
3492 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3494 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3495 if (!item) return FALSE;
3496 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3497 /* nPos is now the position of the item */
3498 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3503 /*******************************************************************
3504 * ModifyMenuW (USER32.@)
3506 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3507 UINT_PTR id, LPCWSTR str )
3511 if (IS_STRING_ITEM(flags))
3513 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3517 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3520 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3521 return MENU_SetItemData( item, flags, id, str );
3525 /*******************************************************************
3526 * ModifyMenuA (USER32.@)
3528 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3529 UINT_PTR id, LPCSTR str )
3533 if (IS_STRING_ITEM(flags) && str)
3535 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3536 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3539 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3540 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3541 HeapFree( GetProcessHeap(), 0, newstr );
3545 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3549 /**********************************************************************
3550 * CreatePopupMenu (USER32.@)
3552 HMENU WINAPI CreatePopupMenu(void)
3557 if (!(hmenu = CreateMenu())) return 0;
3558 menu = MENU_GetMenu( hmenu );
3559 menu->wFlags |= MF_POPUP;
3560 menu->bTimeToHide = FALSE;
3565 /**********************************************************************
3566 * GetMenuCheckMarkDimensions (USER.417)
3567 * GetMenuCheckMarkDimensions (USER32.@)
3569 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3571 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3575 /**********************************************************************
3576 * SetMenuItemBitmaps (USER32.@)
3578 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3579 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3582 TRACE("(%p, %04x, %04x, %p, %p)\n",
3583 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3584 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3586 if (!hNewCheck && !hNewUnCheck)
3588 item->fState &= ~MF_USECHECKBITMAPS;
3590 else /* Install new bitmaps */
3592 item->hCheckBit = hNewCheck;
3593 item->hUnCheckBit = hNewUnCheck;
3594 item->fState |= MF_USECHECKBITMAPS;
3600 /**********************************************************************
3601 * CreateMenu (USER32.@)
3603 HMENU WINAPI CreateMenu(void)
3607 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3608 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3610 ZeroMemory(menu, sizeof(POPUPMENU));
3611 menu->wMagic = MENU_MAGIC;
3612 menu->FocusedItem = NO_SELECTED_ITEM;
3613 menu->bTimeToHide = FALSE;
3615 TRACE("return %p\n", hMenu );
3621 /**********************************************************************
3622 * DestroyMenu (USER32.@)
3624 BOOL WINAPI DestroyMenu( HMENU hMenu )
3626 TRACE("(%p)\n", hMenu);
3628 /* Silently ignore attempts to destroy default system popup */
3630 if (hMenu && hMenu != MENU_DefSysPopup)
3632 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3634 if (!lppop) return FALSE;
3636 lppop->wMagic = 0; /* Mark it as destroyed */
3638 /* DestroyMenu should not destroy system menu popup owner */
3639 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3641 DestroyWindow( lppop->hWnd );
3645 if (lppop->items) /* recursively destroy submenus */
3648 MENUITEM *item = lppop->items;
3649 for (i = lppop->nItems; i > 0; i--, item++)
3651 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3652 MENU_FreeItemData( item );
3654 HeapFree( GetProcessHeap(), 0, lppop->items );
3656 USER_HEAP_FREE( hMenu );
3658 return (hMenu != MENU_DefSysPopup);
3662 /**********************************************************************
3663 * GetSystemMenu (USER32.@)
3665 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3667 WND *wndPtr = WIN_GetPtr( hWnd );
3670 if (wndPtr == WND_OTHER_PROCESS)
3672 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3676 if( wndPtr->hSysMenu )
3680 DestroyMenu(wndPtr->hSysMenu);
3681 wndPtr->hSysMenu = 0;
3685 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3688 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3689 menu->items[0].hSubMenu = MENU_CopySysPopup();
3693 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3694 wndPtr->hSysMenu, hWnd);
3695 wndPtr->hSysMenu = 0;
3700 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3701 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3703 if( wndPtr->hSysMenu )
3706 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3708 /* Store the dummy sysmenu handle to facilitate the refresh */
3709 /* of the close button if the SC_CLOSE item change */
3710 menu = MENU_GetMenu(retvalue);
3712 menu->hSysMenuOwner = wndPtr->hSysMenu;
3714 WIN_ReleasePtr( wndPtr );
3716 return bRevert ? 0 : retvalue;
3720 /*******************************************************************
3721 * SetSystemMenu (USER32.@)
3723 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3725 WND *wndPtr = WIN_GetPtr( hwnd );
3727 if (wndPtr && wndPtr != WND_OTHER_PROCESS)
3729 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3730 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3731 WIN_ReleasePtr( wndPtr );
3738 /**********************************************************************
3739 * GetMenu (USER32.@)
3741 HMENU WINAPI GetMenu( HWND hWnd )
3743 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3744 TRACE("for %p returning %p\n", hWnd, retvalue);
3749 /**********************************************************************
3752 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3753 * SetWindowPos call that would result if SetMenu were called directly.
3755 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3757 TRACE("(%p, %p);\n", hWnd, hMenu);
3759 if (hMenu && !IsMenu(hMenu))
3761 WARN("hMenu %p is not a menu handle\n", hMenu);
3764 if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3766 hWnd = WIN_GetFullHandle( hWnd );
3767 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3773 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3775 lpmenu->hWnd = hWnd;
3776 lpmenu->Height = 0; /* Make sure we recalculate the size */
3778 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3783 /**********************************************************************
3784 * SetMenu (USER32.@)
3786 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3788 if(!MENU_SetMenu(hWnd, hMenu))
3791 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3792 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3797 /**********************************************************************
3798 * GetSubMenu (USER32.@)
3800 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3804 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3805 if (!(lpmi->fType & MF_POPUP)) return 0;
3806 return lpmi->hSubMenu;
3810 /**********************************************************************
3811 * DrawMenuBar (USER32.@)
3813 BOOL WINAPI DrawMenuBar( HWND hWnd )
3816 HMENU hMenu = GetMenu(hWnd);
3818 if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3819 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3821 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3822 lppop->hwndOwner = hWnd;
3823 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3824 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3828 /***********************************************************************
3829 * DrawMenuBarTemp (USER32.@)
3833 * called by W98SE desk.cpl Control Panel Applet
3835 * Not 100% sure about the param names, but close.
3837 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3844 hMenu = GetMenu(hwnd);
3849 lppop = MENU_GetMenu( hMenu );
3850 if (lppop == NULL || lprect == NULL)
3852 retvalue = GetSystemMetrics(SM_CYMENU);
3856 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3858 hfontOld = SelectObject( hDC, hFont);
3860 if (lppop->Height == 0)
3861 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3863 lprect->bottom = lprect->top + lppop->Height;
3865 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3867 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3868 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3869 LineTo( hDC, lprect->right, lprect->bottom );
3871 if (lppop->nItems == 0)
3873 retvalue = GetSystemMetrics(SM_CYMENU);
3877 for (i = 0; i < lppop->nItems; i++)
3879 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3880 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3882 retvalue = lppop->Height;
3885 if (hfontOld) SelectObject (hDC, hfontOld);
3889 /***********************************************************************
3890 * EndMenu (USER.187)
3891 * EndMenu (USER32.@)
3893 void WINAPI EndMenu(void)
3895 /* if we are in the menu code, and it is active */
3896 if (!fEndMenu && top_popup)
3898 /* terminate the menu handling code */
3901 /* needs to be posted to wakeup the internal menu handler */
3902 /* which will now terminate the menu, in the event that */
3903 /* the main window was minimized, or lost focus, so we */
3904 /* don't end up with an orphaned menu */
3905 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3910 /***********************************************************************
3911 * LookupMenuHandle (USER.217)
3913 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3915 HMENU hmenu32 = HMENU_32(hmenu);
3917 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3918 else return HMENU_16(hmenu32);
3922 /**********************************************************************
3923 * LoadMenu (USER.150)
3925 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3931 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3932 if (!name) return 0;
3934 instance = GetExePtr( instance );
3935 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3936 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3937 hMenu = LoadMenuIndirect16(LockResource16(handle));
3938 FreeResource16( handle );
3943 /*****************************************************************
3944 * LoadMenuA (USER32.@)
3946 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3948 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3949 if (!hrsrc) return 0;
3950 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3954 /*****************************************************************
3955 * LoadMenuW (USER32.@)
3957 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3959 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3960 if (!hrsrc) return 0;
3961 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3965 /**********************************************************************
3966 * LoadMenuIndirect (USER.220)
3968 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3971 WORD version, offset;
3972 LPCSTR p = (LPCSTR)template;
3974 TRACE("(%p)\n", template );
3975 version = GET_WORD(p);
3979 WARN("version must be 0 for Win16\n" );
3982 offset = GET_WORD(p);
3983 p += sizeof(WORD) + offset;
3984 if (!(hMenu = CreateMenu())) return 0;
3985 if (!MENU_ParseResource( p, hMenu, FALSE ))
3987 DestroyMenu( hMenu );
3990 return HMENU_16(hMenu);
3994 /**********************************************************************
3995 * LoadMenuIndirectW (USER32.@)
3997 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4000 WORD version, offset;
4001 LPCSTR p = (LPCSTR)template;
4003 version = GET_WORD(p);
4005 TRACE("%p, ver %d\n", template, version );
4008 case 0: /* standard format is version of 0 */
4009 offset = GET_WORD(p);
4010 p += sizeof(WORD) + offset;
4011 if (!(hMenu = CreateMenu())) return 0;
4012 if (!MENU_ParseResource( p, hMenu, TRUE ))
4014 DestroyMenu( hMenu );
4018 case 1: /* extended format is version of 1 */
4019 offset = GET_WORD(p);
4020 p += sizeof(WORD) + offset;
4021 if (!(hMenu = CreateMenu())) return 0;
4022 if (!MENUEX_ParseResource( p, hMenu))
4024 DestroyMenu( hMenu );
4029 ERR("version %d not supported.\n", version);
4035 /**********************************************************************
4036 * LoadMenuIndirectA (USER32.@)
4038 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4040 return LoadMenuIndirectW( template );
4044 /**********************************************************************
4047 BOOL WINAPI IsMenu(HMENU hmenu)
4049 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4050 return menu != NULL;
4053 /**********************************************************************
4054 * GetMenuItemInfo_common
4057 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4058 LPMENUITEMINFOW lpmii, BOOL unicode)
4060 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4062 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4067 if (lpmii->fMask & MIIM_TYPE) {
4068 lpmii->fType = menu->fType;
4069 switch (MENU_ITEM_TYPE(menu->fType)) {
4071 break; /* will be done below */
4074 lpmii->dwTypeData = menu->text;
4081 /* copy the text string */
4082 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4083 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4088 len = strlenW(menu->text);
4089 if(lpmii->dwTypeData && lpmii->cch)
4090 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4094 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4095 if(lpmii->dwTypeData && lpmii->cch)
4096 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4097 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4098 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4100 /* if we've copied a substring we return its length */
4101 if(lpmii->dwTypeData && lpmii->cch)
4103 if (lpmii->cch <= len) lpmii->cch--;
4105 else /* return length of string */
4109 if (lpmii->fMask & MIIM_FTYPE)
4110 lpmii->fType = menu->fType;
4112 if (lpmii->fMask & MIIM_BITMAP)
4113 lpmii->hbmpItem = menu->hbmpItem;
4115 if (lpmii->fMask & MIIM_STATE)
4116 lpmii->fState = menu->fState;
4118 if (lpmii->fMask & MIIM_ID)
4119 lpmii->wID = menu->wID;
4121 if (lpmii->fMask & MIIM_SUBMENU)
4122 lpmii->hSubMenu = menu->hSubMenu;
4124 if (lpmii->fMask & MIIM_CHECKMARKS) {
4125 lpmii->hbmpChecked = menu->hCheckBit;
4126 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4128 if (lpmii->fMask & MIIM_DATA)
4129 lpmii->dwItemData = menu->dwItemData;
4134 /**********************************************************************
4135 * GetMenuItemInfoA (USER32.@)
4137 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4138 LPMENUITEMINFOA lpmii)
4140 return GetMenuItemInfo_common (hmenu, item, bypos,
4141 (LPMENUITEMINFOW)lpmii, FALSE);
4144 /**********************************************************************
4145 * GetMenuItemInfoW (USER32.@)
4147 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4148 LPMENUITEMINFOW lpmii)
4150 return GetMenuItemInfo_common (hmenu, item, bypos,
4155 /* set a menu item text from a ASCII or Unicode string */
4156 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4161 menu->fType |= MF_SEPARATOR;
4165 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4166 strcpyW( menu->text, text );
4170 LPCSTR str = (LPCSTR)text;
4171 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4172 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4173 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4178 /**********************************************************************
4179 * SetMenuItemInfo_common
4182 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4183 const MENUITEMINFOW *lpmii,
4186 if (!menu) return FALSE;
4188 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4190 if (lpmii->fMask & MIIM_TYPE ) {
4191 /* Get rid of old string. */
4192 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4193 HeapFree(GetProcessHeap(), 0, menu->text);
4197 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4198 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4199 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4201 menu->text = lpmii->dwTypeData;
4203 if (IS_STRING_ITEM(menu->fType))
4204 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4207 if (lpmii->fMask & MIIM_FTYPE ) {
4208 /* free the string when the type is changing */
4209 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4210 HeapFree(GetProcessHeap(), 0, menu->text);
4213 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4214 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4215 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4216 menu->fType |= MF_SEPARATOR;
4219 if (lpmii->fMask & MIIM_STRING ) {
4220 if (IS_STRING_ITEM(menu->fType)) {
4221 /* free the string when used */
4222 HeapFree(GetProcessHeap(), 0, menu->text);
4223 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4227 if (lpmii->fMask & MIIM_STATE)
4229 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4230 menu->fState = lpmii->fState;
4233 if (lpmii->fMask & MIIM_ID)
4234 menu->wID = lpmii->wID;
4236 if (lpmii->fMask & MIIM_SUBMENU) {
4237 menu->hSubMenu = lpmii->hSubMenu;
4238 if (menu->hSubMenu) {
4239 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4241 subMenu->wFlags |= MF_POPUP;
4242 menu->fType |= MF_POPUP;
4245 /* FIXME: Return an error ? */
4246 menu->fType &= ~MF_POPUP;
4249 menu->fType &= ~MF_POPUP;
4252 if (lpmii->fMask & MIIM_CHECKMARKS)
4254 if (lpmii->fType & MFT_RADIOCHECK)
4255 menu->fType |= MFT_RADIOCHECK;
4257 menu->hCheckBit = lpmii->hbmpChecked;
4258 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4260 if (lpmii->fMask & MIIM_DATA)
4261 menu->dwItemData = lpmii->dwItemData;
4263 if (lpmii->fMask & MIIM_BITMAP)
4264 menu->hbmpItem = lpmii->hbmpItem;
4266 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4270 /**********************************************************************
4271 * SetMenuItemInfoA (USER32.@)
4273 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4274 const MENUITEMINFOA *lpmii)
4276 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4277 (const MENUITEMINFOW *)lpmii, FALSE);
4280 /**********************************************************************
4281 * SetMenuItemInfoW (USER32.@)
4283 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4284 const MENUITEMINFOW *lpmii)
4286 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4290 /**********************************************************************
4291 * SetMenuDefaultItem (USER32.@)
4294 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4300 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4302 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4304 /* reset all default-item flags */
4306 for (i = 0; i < menu->nItems; i++, item++)
4308 item->fState &= ~MFS_DEFAULT;
4311 /* no default item */
4320 if ( uItem >= menu->nItems ) return FALSE;
4321 item[uItem].fState |= MFS_DEFAULT;
4326 for (i = 0; i < menu->nItems; i++, item++)
4328 if (item->wID == uItem)
4330 item->fState |= MFS_DEFAULT;
4339 /**********************************************************************
4340 * GetMenuDefaultItem (USER32.@)
4342 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4348 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4350 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4352 /* find default item */
4356 if (! item) return -1;
4358 while ( !( item->fState & MFS_DEFAULT ) )
4361 if (i >= menu->nItems ) return -1;
4364 /* default: don't return disabled items */
4365 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4367 /* search rekursiv when needed */
4368 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4371 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4372 if ( -1 != ret ) return ret;
4374 /* when item not found in submenu, return the popup item */
4376 return ( bypos ) ? i : item->wID;
4381 /**********************************************************************
4382 * InsertMenuItemA (USER32.@)
4384 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4385 const MENUITEMINFOA *lpmii)
4387 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4388 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4392 /**********************************************************************
4393 * InsertMenuItemW (USER32.@)
4395 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4396 const MENUITEMINFOW *lpmii)
4398 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4399 return SetMenuItemInfo_common(item, lpmii, TRUE);
4402 /**********************************************************************
4403 * CheckMenuRadioItem (USER32.@)
4406 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4407 UINT first, UINT last, UINT check,
4410 MENUITEM *mifirst, *milast, *micheck;
4411 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4413 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4415 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4416 milast = MENU_FindItem (&mlast, &last, bypos);
4417 micheck = MENU_FindItem (&mcheck, &check, bypos);
4419 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4420 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4421 micheck > milast || micheck < mifirst)
4424 while (mifirst <= milast)
4426 if (mifirst == micheck)
4428 mifirst->fType |= MFT_RADIOCHECK;
4429 mifirst->fState |= MFS_CHECKED;
4431 mifirst->fType &= ~MFT_RADIOCHECK;
4432 mifirst->fState &= ~MFS_CHECKED;
4441 /**********************************************************************
4442 * GetMenuItemRect (USER32.@)
4444 * ATTENTION: Here, the returned values in rect are the screen
4445 * coordinates of the item just like if the menu was
4446 * always on the upper left side of the application.
4449 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4452 POPUPMENU *itemMenu;
4456 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4458 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4459 referenceHwnd = hwnd;
4463 itemMenu = MENU_GetMenu(hMenu);
4464 if (itemMenu == NULL)
4467 if(itemMenu->hWnd == 0)
4469 referenceHwnd = itemMenu->hWnd;
4472 if ((rect == NULL) || (item == NULL))
4477 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4483 /**********************************************************************
4484 * SetMenuInfo (USER32.@)
4487 * MIM_APPLYTOSUBMENUS
4488 * actually use the items to draw the menu
4490 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4494 TRACE("(%p %p)\n", hMenu, lpmi);
4496 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4499 if (lpmi->fMask & MIM_BACKGROUND)
4500 menu->hbrBack = lpmi->hbrBack;
4502 if (lpmi->fMask & MIM_HELPID)
4503 menu->dwContextHelpID = lpmi->dwContextHelpID;
4505 if (lpmi->fMask & MIM_MAXHEIGHT)
4506 menu->cyMax = lpmi->cyMax;
4508 if (lpmi->fMask & MIM_MENUDATA)
4509 menu->dwMenuData = lpmi->dwMenuData;
4511 if (lpmi->fMask & MIM_STYLE)
4513 menu->dwStyle = lpmi->dwStyle;
4514 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4515 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4516 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4517 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4518 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4526 /**********************************************************************
4527 * GetMenuInfo (USER32.@)
4533 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4536 TRACE("(%p %p)\n", hMenu, lpmi);
4538 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4541 if (lpmi->fMask & MIM_BACKGROUND)
4542 lpmi->hbrBack = menu->hbrBack;
4544 if (lpmi->fMask & MIM_HELPID)
4545 lpmi->dwContextHelpID = menu->dwContextHelpID;
4547 if (lpmi->fMask & MIM_MAXHEIGHT)
4548 lpmi->cyMax = menu->cyMax;
4550 if (lpmi->fMask & MIM_MENUDATA)
4551 lpmi->dwMenuData = menu->dwMenuData;
4553 if (lpmi->fMask & MIM_STYLE)
4554 lpmi->dwStyle = menu->dwStyle;
4562 /**********************************************************************
4563 * SetMenuContextHelpId (USER32.@)
4565 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4569 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4571 if ((menu = MENU_GetMenu(hMenu)))
4573 menu->dwContextHelpID = dwContextHelpID;
4580 /**********************************************************************
4581 * GetMenuContextHelpId (USER32.@)
4583 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4587 TRACE("(%p)\n", hMenu);
4589 if ((menu = MENU_GetMenu(hMenu)))
4591 return menu->dwContextHelpID;
4596 /**********************************************************************
4597 * MenuItemFromPoint (USER32.@)
4599 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4601 POPUPMENU *menu = MENU_GetMenu(hMenu);
4604 /*FIXME: Do we have to handle hWnd here? */
4605 if (!menu) return -1;
4606 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4611 /**********************************************************************
4612 * translate_accelerator
4614 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4615 BYTE fVirt, WORD key, WORD cmd )
4620 if (wParam != key) return FALSE;
4622 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4623 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4624 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4626 if (message == WM_CHAR || message == WM_SYSCHAR)
4628 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4630 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4636 if(fVirt & FVIRTKEY)
4638 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4639 wParam, 0xff & HIWORD(lParam));
4641 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4642 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4646 if (!(lParam & 0x01000000)) /* no special_key */
4648 if ((fVirt & FALT) && (lParam & 0x20000000))
4649 { /* ^^ ALT pressed */
4650 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4659 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4663 HMENU hMenu, hSubMenu, hSysMenu;
4664 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4666 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4667 hSysMenu = get_win_sys_menu( hWnd );
4669 /* find menu item and ask application to initialize it */
4670 /* 1. in the system menu */
4671 hSubMenu = hSysMenu;
4673 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4677 if (!IsWindowEnabled(hWnd))
4681 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4682 if(hSubMenu != hSysMenu)
4684 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4685 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4686 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4688 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4691 else /* 2. in the window's menu */
4695 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4699 if (!IsWindowEnabled(hWnd))
4703 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4704 if(hSubMenu != hMenu)
4706 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4707 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4708 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4710 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4717 if (uSysStat != (UINT)-1)
4719 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4726 if (uStat != (UINT)-1)
4732 if (uStat & (MF_DISABLED|MF_GRAYED))
4744 if( mesg==WM_COMMAND )
4746 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4747 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4749 else if( mesg==WM_SYSCOMMAND )
4751 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4752 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4756 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4757 * #0: unknown (please report!)
4758 * #1: for WM_KEYUP,WM_SYSKEYUP
4759 * #2: mouse is captured
4760 * #3: window is disabled
4761 * #4: it's a disabled system menu option
4762 * #5: it's a menu option, but window is iconic
4763 * #6: it's a menu option, but disabled
4765 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4767 ERR_(accel)(" unknown reason - please report!\n");
4772 /**********************************************************************
4773 * TranslateAccelerator (USER32.@)
4774 * TranslateAcceleratorA (USER32.@)
4776 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4779 LPACCEL16 lpAccelTbl;
4783 if (!hWnd || !msg) return 0;
4785 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4787 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4791 wParam = msg->wParam;
4793 switch (msg->message)
4802 char ch = LOWORD(wParam);
4804 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4805 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4813 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4814 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4818 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4819 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4821 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4826 /**********************************************************************
4827 * TranslateAcceleratorW (USER32.@)
4829 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4832 LPACCEL16 lpAccelTbl;
4835 if (!hWnd || !msg) return 0;
4837 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4839 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4843 switch (msg->message)
4855 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4856 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4860 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4861 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4863 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);