d3d9/tests: Use a separate device for test_vshader_input().
[wine/wine-gecko.git] / dlls / user32 / menu.c
blob8bb508dfd374308bdcb374a304765c4620f02371
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class =
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 const char *postfix)
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
229 if (mp) {
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
232 if ( mp->hSubMenu)
233 TRACE( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 TRACE( ", fType=");
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 if (flags)
248 TRACE( "+0x%x", flags);
250 flags = mp->fState;
251 if (flags) {
252 int count = 0;
253 TRACE( ", State=");
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
261 if (flags)
262 TRACE( "+0x%x", flags);
264 if (mp->hCheckBit)
265 TRACE( ", Chk=%p", mp->hCheckBit);
266 if (mp->hUnCheckBit)
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 if (mp->text)
269 TRACE( ", Text=%s", debugstr_w(mp->text));
270 if (mp->dwItemData)
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if (mp->hbmpItem)
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 else
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
279 TRACE( " }");
280 } else
281 TRACE( "NULL");
282 TRACE(" %s\n", postfix);
285 #undef MENUOUT
286 #undef MENUFLAG
289 /***********************************************************************
290 * MENU_GetMenu
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
298 if (menu == OBJ_OTHER_PROCESS)
300 WARN( "other process menu %p?\n", hMenu);
301 return NULL;
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
305 return menu;
308 /***********************************************************************
309 * get_win_sys_menu
311 * Get the system menu of a window
313 static HMENU get_win_sys_menu( HWND hwnd )
315 HMENU ret = 0;
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
319 ret = win->hSysMenu;
320 WIN_ReleasePtr( win );
322 return ret;
325 /***********************************************************************
326 * get_menu_font
328 static HFONT get_menu_font( BOOL bold )
330 static HFONT hMenuFont, hMenuFontBold;
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
334 if (!ret)
336 NONCLIENTMETRICSW ncm;
337 HFONT prev;
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
342 if (bold)
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 ret, NULL );
350 if (prev)
352 /* another thread beat us to it */
353 DeleteObject( ret );
354 ret = prev;
357 return ret;
360 /***********************************************************************
361 * get_arrow_bitmap
363 static HBITMAP get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap;
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 return arrow_bitmap;
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap;
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
379 return arrow_bitmap;
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap;
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
390 return arrow_bitmap;
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap;
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
401 return arrow_bitmap;
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap;
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
412 return arrow_bitmap;
415 /***********************************************************************
416 * MENU_CopySysPopup
418 * Return the default system menu.
420 static HMENU MENU_CopySysPopup(BOOL mdi)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
426 if( hMenu ) {
427 MENUINFO minfo;
428 MENUITEMINFOW miteminfo;
429 POPUPMENU* menu = MENU_GetMenu(hMenu);
430 menu->wFlags |= MF_SYSMENU | MF_POPUP;
431 /* decorate the menu with bitmaps */
432 minfo.cbSize = sizeof( MENUINFO);
433 minfo.dwStyle = MNS_CHECKORBMP;
434 minfo.fMask = MIM_STYLE;
435 SetMenuInfo( hMenu, &minfo);
436 miteminfo.cbSize = sizeof( MENUITEMINFOW);
437 miteminfo.fMask = MIIM_BITMAP;
438 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
439 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
440 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
441 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
443 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
446 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
448 else
449 ERR("Unable to load default system menu\n" );
451 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
453 return hMenu;
457 /**********************************************************************
458 * MENU_GetSysMenu
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
466 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
468 HMENU hMenu;
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
471 if ((hMenu = CreateMenu()))
473 POPUPMENU *menu = MENU_GetMenu(hMenu);
474 menu->wFlags = MF_SYSMENU;
475 menu->hWnd = WIN_GetFullHandle( hWnd );
476 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
478 if (!hPopupMenu)
480 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
481 hPopupMenu = MENU_CopySysPopup(TRUE);
482 else
483 hPopupMenu = MENU_CopySysPopup(FALSE);
486 if (hPopupMenu)
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
499 return hMenu;
501 DestroyMenu( hMenu );
503 ERR("failed to load system menu!\n");
504 return 0;
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
515 BOOL gray;
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
529 /* The menu item must keep its state if it's disabled */
530 if(gray)
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
538 * HMENU hMenu )
540 *****************************************************************************/
542 static UINT MENU_GetStartOfNextColumn(
543 HMENU hMenu )
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 UINT i;
548 if(!menu)
549 return NO_SELECTED_ITEM;
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
553 return i;
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return i;
560 return NO_SELECTED_ITEM;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
567 * HMENU hMenu )
569 *****************************************************************************/
571 static UINT MENU_GetStartOfPrevColumn(
572 HMENU hMenu )
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 UINT i;
577 if( !menu )
578 return NO_SELECTED_ITEM;
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
583 /* Find the start of the column */
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 --i); /* empty */
589 if(i == 0)
590 return NO_SELECTED_ITEM;
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 break;
597 TRACE("ret %d.\n", i );
599 return i;
604 /***********************************************************************
605 * MENU_FindItem
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
612 POPUPMENU *menu;
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
615 UINT i;
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
623 else
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
628 if (item->fType & MF_POPUP)
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
632 if (subitem)
634 *hmenu = hsubmenu;
635 return subitem;
637 else if (item->wID == *nPos)
639 /* fallback to this item if nothing else found */
640 fallback_pos = i;
641 fallback = item;
644 else if (item->wID == *nPos)
646 *nPos = i;
647 return item;
652 if (fallback)
653 *nPos = fallback_pos;
655 return fallback;
658 /***********************************************************************
659 * MENU_FindSubMenu
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
667 POPUPMENU *menu;
668 UINT i;
669 MENUITEM *item;
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
673 item = menu->items;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
677 return i;
679 else {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
683 *hmenu = hsubmenu;
684 return pos;
688 return NO_SELECTED_ITEM;
691 /***********************************************************************
692 * MENU_FreeItemData
694 static void MENU_FreeItemData( MENUITEM* item )
696 /* delete text */
697 HeapFree( GetProcessHeap(), 0, item->text );
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
705 static void
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
708 if (menu->bScrolling)
710 UINT arrow_bitmap_height;
711 BITMAP bmp;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
731 MENUITEM *item;
732 UINT i;
733 RECT rect;
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
737 else pt.x -= rect.left;
738 pt.y -= rect.top;
739 item = menu->items;
740 for (i = 0; i < menu->nItems; i++, item++)
742 rect = item->rect;
743 MENU_AdjustMenuItemRect(menu, &rect);
744 if (PtInRect(&rect, pt))
746 if (pos) *pos = i;
747 return item;
750 return NULL;
754 /***********************************************************************
755 * MENU_FindItemByKey
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
760 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
761 WCHAR key, BOOL forceMenuChar )
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
765 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
767 if (hmenu)
769 POPUPMENU *menu = MENU_GetMenu( hmenu );
770 MENUITEM *item = menu->items;
771 LRESULT menuchar;
773 if( !forceMenuChar )
775 UINT i;
776 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
778 for (i = 0; i < menu->nItems; i++, item++)
780 if( item->text)
782 const WCHAR *p = item->text - 2;
785 const WCHAR *q = p + 2;
786 p = strchrW (q, '&');
787 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
789 while (p != NULL && p [1] == '&');
790 if (p && (toupperW(p[1]) == toupperW(key))) return i;
794 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
795 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
796 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
797 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
799 return (UINT)(-1);
803 /***********************************************************************
804 * MENU_GetBitmapItemSize
806 * Get the size of a bitmap item.
808 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
809 HWND hwndOwner)
811 BITMAP bm;
812 HBITMAP bmp = lpitem->hbmpItem;
814 size->cx = size->cy = 0;
816 /* check if there is a magic menu item associated with this item */
817 switch( (INT_PTR) bmp )
819 case (INT_PTR)HBMMENU_CALLBACK:
821 MEASUREITEMSTRUCT measItem;
822 measItem.CtlType = ODT_MENU;
823 measItem.CtlID = 0;
824 measItem.itemID = lpitem->wID;
825 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
826 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
827 measItem.itemData = lpitem->dwItemData;
828 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
829 size->cx = measItem.itemWidth;
830 size->cy = measItem.itemHeight;
831 return;
833 break;
834 case (INT_PTR)HBMMENU_SYSTEM:
835 if (lpitem->dwItemData)
837 bmp = (HBITMAP)lpitem->dwItemData;
838 break;
840 /* fall through */
841 case (INT_PTR)HBMMENU_MBAR_RESTORE:
842 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
843 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
844 case (INT_PTR)HBMMENU_MBAR_CLOSE:
845 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
846 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
847 size->cy = size->cx;
848 return;
849 case (INT_PTR)HBMMENU_POPUP_CLOSE:
850 case (INT_PTR)HBMMENU_POPUP_RESTORE:
851 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
852 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
853 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
854 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
855 return;
857 if (GetObjectW(bmp, sizeof(bm), &bm ))
859 size->cx = bm.bmWidth;
860 size->cy = bm.bmHeight;
864 /***********************************************************************
865 * MENU_DrawBitmapItem
867 * Draw a bitmap item.
869 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
870 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
872 BITMAP bm;
873 DWORD rop;
874 HDC hdcMem;
875 HBITMAP bmp;
876 int w = rect->right - rect->left;
877 int h = rect->bottom - rect->top;
878 int bmp_xoffset = 0;
879 int left, top;
880 HBITMAP hbmToDraw = lpitem->hbmpItem;
881 bmp = hbmToDraw;
883 /* Check if there is a magic menu item associated with this item */
884 if (IS_MAGIC_BITMAP(hbmToDraw))
886 UINT flags = 0;
887 WCHAR bmchr = 0;
888 RECT r;
890 switch((INT_PTR)hbmToDraw)
892 case (INT_PTR)HBMMENU_SYSTEM:
893 if (lpitem->dwItemData)
895 bmp = (HBITMAP)lpitem->dwItemData;
896 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 else
900 static HBITMAP hBmpSysMenu;
902 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
903 bmp = hBmpSysMenu;
904 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
905 /* only use right half of the bitmap */
906 bmp_xoffset = bm.bmWidth / 2;
907 bm.bmWidth -= bmp_xoffset;
909 goto got_bitmap;
910 case (INT_PTR)HBMMENU_MBAR_RESTORE:
911 flags = DFCS_CAPTIONRESTORE;
912 break;
913 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
914 flags = DFCS_CAPTIONMIN;
915 break;
916 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
917 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
918 break;
919 case (INT_PTR)HBMMENU_MBAR_CLOSE:
920 flags = DFCS_CAPTIONCLOSE;
921 break;
922 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
923 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
924 break;
925 case (INT_PTR)HBMMENU_CALLBACK:
927 DRAWITEMSTRUCT drawItem;
928 drawItem.CtlType = ODT_MENU;
929 drawItem.CtlID = 0;
930 drawItem.itemID = lpitem->wID;
931 drawItem.itemAction = odaction;
932 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
933 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
934 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
935 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
936 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
937 drawItem.hwndItem = (HWND)hmenu;
938 drawItem.hDC = hdc;
939 drawItem.itemData = lpitem->dwItemData;
940 drawItem.rcItem = *rect;
941 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
942 return;
944 break;
945 case (INT_PTR)HBMMENU_POPUP_CLOSE:
946 bmchr = 0x72;
947 break;
948 case (INT_PTR)HBMMENU_POPUP_RESTORE:
949 bmchr = 0x32;
950 break;
951 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
952 bmchr = 0x31;
953 break;
954 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
955 bmchr = 0x30;
956 break;
957 default:
958 FIXME("Magic %p not implemented\n", hbmToDraw);
959 return;
961 if (bmchr)
963 /* draw the magic bitmaps using marlett font characters */
964 /* FIXME: fontsize and the position (x,y) could probably be better */
965 HFONT hfont, hfontsav;
966 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
967 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
968 { 'M','a','r','l','e','t','t',0 } };
969 logfont.lfHeight = min( h, w) - 5 ;
970 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
971 hfont = CreateFontIndirectW( &logfont);
972 hfontsav = SelectObject(hdc, hfont);
973 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
974 SelectObject(hdc, hfontsav);
975 DeleteObject( hfont);
977 else
979 r = *rect;
980 InflateRect( &r, -1, -1 );
981 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
982 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
984 return;
987 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
989 got_bitmap:
990 hdcMem = CreateCompatibleDC( hdc );
991 SelectObject( hdcMem, bmp );
993 /* handle fontsize > bitmap_height */
994 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
995 left=rect->left;
996 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
997 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
998 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
999 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1000 DeleteDC( hdcMem );
1004 /***********************************************************************
1005 * MENU_CalcItemSize
1007 * Calculate the size of the menu item and store it in lpitem->rect.
1009 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1010 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1012 WCHAR *p;
1013 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1014 UINT arrow_bitmap_width;
1015 BITMAP bm;
1016 INT itemheight;
1018 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1019 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1020 (menuBar ? " (MenuBar)" : ""));
1022 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1023 arrow_bitmap_width = bm.bmWidth;
1025 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1026 if( !menucharsize.cx ) {
1027 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1028 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1029 * but it is unlikely an application will depend on that */
1030 ODitemheight = HIWORD( GetDialogBaseUnits());
1033 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1035 if (lpitem->fType & MF_OWNERDRAW)
1037 MEASUREITEMSTRUCT mis;
1038 mis.CtlType = ODT_MENU;
1039 mis.CtlID = 0;
1040 mis.itemID = lpitem->wID;
1041 mis.itemData = lpitem->dwItemData;
1042 mis.itemHeight = ODitemheight;
1043 mis.itemWidth = 0;
1044 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1045 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1046 * width of a menufont character to the width of an owner-drawn menu.
1048 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1049 if (menuBar) {
1050 /* under at least win95 you seem to be given a standard
1051 height for the menu and the height value is ignored */
1052 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1053 } else
1054 lpitem->rect.bottom += mis.itemHeight;
1056 TRACE("id=%04lx size=%dx%d\n",
1057 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1058 lpitem->rect.bottom-lpitem->rect.top);
1059 return;
1062 if (lpitem->fType & MF_SEPARATOR)
1064 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1065 if( !menuBar)
1066 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1067 return;
1070 itemheight = 0;
1071 lpitem->xTab = 0;
1073 if (!menuBar) {
1074 if (lpitem->hbmpItem) {
1075 SIZE size;
1077 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1078 /* Keep the size of the bitmap in callback mode to be able
1079 * to draw it correctly */
1080 lpitem->bmpsize = size;
1081 lppop->textOffset = max( lppop->textOffset, size.cx);
1082 lpitem->rect.right += size.cx + 2;
1083 itemheight = size.cy + 2;
1085 if( !(lppop->dwStyle & MNS_NOCHECK))
1086 lpitem->rect.right += check_bitmap_width;
1087 lpitem->rect.right += 4 + menucharsize.cx;
1088 lpitem->xTab = lpitem->rect.right;
1089 lpitem->rect.right += arrow_bitmap_width;
1090 } else if (lpitem->hbmpItem) { /* menuBar */
1091 SIZE size;
1093 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1094 lpitem->bmpsize = size;
1095 lpitem->rect.right += size.cx;
1096 if( lpitem->text) lpitem->rect.right += 2;
1097 itemheight = size.cy;
1100 /* it must be a text item - unless it's the system menu */
1101 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1102 HFONT hfontOld = NULL;
1103 RECT rc = lpitem->rect;
1104 LONG txtheight, txtwidth;
1106 if ( lpitem->fState & MFS_DEFAULT ) {
1107 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1109 if (menuBar) {
1110 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1111 DT_SINGLELINE|DT_CALCRECT);
1112 lpitem->rect.right += rc.right - rc.left;
1113 itemheight = max( max( itemheight, txtheight),
1114 GetSystemMetrics( SM_CYMENU) - 1);
1115 lpitem->rect.right += 2 * menucharsize.cx;
1116 } else {
1117 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1118 RECT tmprc = rc;
1119 LONG tmpheight;
1120 int n = (int)( p - lpitem->text);
1121 /* Item contains a tab (only meaningful in popup menus) */
1122 /* get text size before the tab */
1123 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1124 DT_SINGLELINE|DT_CALCRECT);
1125 txtwidth = rc.right - rc.left;
1126 p += 1; /* advance past the Tab */
1127 /* get text size after the tab */
1128 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 lpitem->xTab += txtwidth;
1131 txtheight = max( txtheight, tmpheight);
1132 txtwidth += menucharsize.cx + /* space for the tab */
1133 tmprc.right - tmprc.left; /* space for the short cut */
1134 } else {
1135 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1136 DT_SINGLELINE|DT_CALCRECT);
1137 txtwidth = rc.right - rc.left;
1138 lpitem->xTab += txtwidth;
1140 lpitem->rect.right += 2 + txtwidth;
1141 itemheight = max( itemheight,
1142 max( txtheight + 2, menucharsize.cy + 4));
1144 if (hfontOld) SelectObject (hdc, hfontOld);
1145 } else if( menuBar) {
1146 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1148 lpitem->rect.bottom += itemheight;
1149 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1153 /***********************************************************************
1154 * MENU_GetMaxPopupHeight
1156 static UINT
1157 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1159 if (lppop->cyMax)
1160 return lppop->cyMax;
1161 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1165 /***********************************************************************
1166 * MENU_PopupMenuCalcSize
1168 * Calculate the size of a popup menu.
1170 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1172 MENUITEM *lpitem;
1173 HDC hdc;
1174 UINT start, i;
1175 BOOL textandbmp = FALSE;
1176 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1178 lppop->Width = lppop->Height = 0;
1179 if (lppop->nItems == 0) return;
1180 hdc = GetDC( 0 );
1182 SelectObject( hdc, get_menu_font(FALSE));
1184 start = 0;
1185 maxX = 2 + 1;
1187 lppop->textOffset = 0;
1189 while (start < lppop->nItems)
1191 lpitem = &lppop->items[start];
1192 orgX = maxX;
1193 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1194 orgX += MENU_COL_SPACE;
1195 orgY = MENU_TOP_MARGIN;
1197 maxTab = maxTabWidth = 0;
1198 /* Parse items until column break or end of menu */
1199 for (i = start; i < lppop->nItems; i++, lpitem++)
1201 if ((i != start) &&
1202 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1204 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1205 maxX = max( maxX, lpitem->rect.right );
1206 orgY = lpitem->rect.bottom;
1207 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1209 maxTab = max( maxTab, lpitem->xTab );
1210 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1212 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1215 /* Finish the column (set all items to the largest width found) */
1216 maxX = max( maxX, maxTab + maxTabWidth );
1217 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1219 lpitem->rect.right = maxX;
1220 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1221 lpitem->xTab = maxTab;
1224 lppop->Height = max( lppop->Height, orgY );
1227 lppop->Width = maxX;
1228 /* if none of the items have both text and bitmap then
1229 * the text and bitmaps are all aligned on the left. If there is at
1230 * least one item with both text and bitmap then bitmaps are
1231 * on the left and texts left aligned with the right hand side
1232 * of the bitmaps */
1233 if( !textandbmp) lppop->textOffset = 0;
1235 /* space for 3d border */
1236 lppop->Height += MENU_BOTTOM_MARGIN;
1237 lppop->Width += 2;
1239 /* Adjust popup height if it exceeds maximum */
1240 maxHeight = MENU_GetMaxPopupHeight(lppop);
1241 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1242 if (lppop->Height >= maxHeight)
1244 lppop->Height = maxHeight;
1245 lppop->bScrolling = TRUE;
1247 else
1249 lppop->bScrolling = FALSE;
1252 ReleaseDC( 0, hdc );
1256 /***********************************************************************
1257 * MENU_MenuBarCalcSize
1259 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1260 * height is off by 1 pixel which causes lengthy window relocations when
1261 * active document window is maximized/restored.
1263 * Calculate the size of the menu bar.
1265 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1266 LPPOPUPMENU lppop, HWND hwndOwner )
1268 MENUITEM *lpitem;
1269 UINT start, i, helpPos;
1270 int orgX, orgY, maxY;
1272 if ((lprect == NULL) || (lppop == NULL)) return;
1273 if (lppop->nItems == 0) return;
1274 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1275 lppop->Width = lprect->right - lprect->left;
1276 lppop->Height = 0;
1277 maxY = lprect->top+1;
1278 start = 0;
1279 helpPos = ~0U;
1280 lppop->textOffset = 0;
1281 while (start < lppop->nItems)
1283 lpitem = &lppop->items[start];
1284 orgX = lprect->left;
1285 orgY = maxY;
1287 /* Parse items until line break or end of menu */
1288 for (i = start; i < lppop->nItems; i++, lpitem++)
1290 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1291 if ((i != start) &&
1292 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1294 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1295 debug_print_menuitem (" item: ", lpitem, "");
1296 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1298 if (lpitem->rect.right > lprect->right)
1300 if (i != start) break;
1301 else lpitem->rect.right = lprect->right;
1303 maxY = max( maxY, lpitem->rect.bottom );
1304 orgX = lpitem->rect.right;
1307 /* Finish the line (set all items to the largest height found) */
1308 while (start < i) lppop->items[start++].rect.bottom = maxY;
1311 lprect->bottom = maxY;
1312 lppop->Height = lprect->bottom - lprect->top;
1314 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1315 /* the last item (if several lines, only move the last line) */
1316 if (helpPos == ~0U) return;
1317 lpitem = &lppop->items[lppop->nItems-1];
1318 orgY = lpitem->rect.top;
1319 orgX = lprect->right;
1320 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1321 if (lpitem->rect.top != orgY) break; /* Other line */
1322 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1323 lpitem->rect.left += orgX - lpitem->rect.right;
1324 lpitem->rect.right = orgX;
1325 orgX = lpitem->rect.left;
1330 /***********************************************************************
1331 * MENU_DrawScrollArrows
1333 * Draw scroll arrows.
1335 static void
1336 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1338 HDC hdcMem = CreateCompatibleDC(hdc);
1339 HBITMAP hOrigBitmap;
1340 UINT arrow_bitmap_width, arrow_bitmap_height;
1341 BITMAP bmp;
1342 RECT rect;
1344 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1345 arrow_bitmap_width = bmp.bmWidth;
1346 arrow_bitmap_height = bmp.bmHeight;
1349 if (lppop->nScrollPos)
1350 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1351 else
1352 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1353 rect.left = 0;
1354 rect.top = 0;
1355 rect.right = lppop->Width;
1356 rect.bottom = arrow_bitmap_height;
1357 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1358 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1359 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1360 rect.top = lppop->Height - arrow_bitmap_height;
1361 rect.bottom = lppop->Height;
1362 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1363 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1364 SelectObject(hdcMem, get_down_arrow_bitmap());
1365 else
1366 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1367 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1368 lppop->Height - arrow_bitmap_height,
1369 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1370 SelectObject(hdcMem, hOrigBitmap);
1371 DeleteDC(hdcMem);
1375 /***********************************************************************
1376 * draw_popup_arrow
1378 * Draws the popup-menu arrow.
1380 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1381 UINT arrow_bitmap_height)
1383 HDC hdcMem = CreateCompatibleDC( hdc );
1384 HBITMAP hOrigBitmap;
1386 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1387 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1388 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1389 arrow_bitmap_width, arrow_bitmap_height,
1390 hdcMem, 0, 0, SRCCOPY );
1391 SelectObject( hdcMem, hOrigBitmap );
1392 DeleteDC( hdcMem );
1394 /***********************************************************************
1395 * MENU_DrawMenuItem
1397 * Draw a single menu item.
1399 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1400 UINT height, BOOL menuBar, UINT odaction )
1402 RECT rect;
1403 BOOL flat_menu = FALSE;
1404 int bkgnd;
1405 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1406 POPUPMENU *menu = MENU_GetMenu(hmenu);
1407 RECT bmprc;
1409 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1411 if (!menuBar) {
1412 BITMAP bmp;
1413 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1414 arrow_bitmap_width = bmp.bmWidth;
1415 arrow_bitmap_height = bmp.bmHeight;
1418 if (lpitem->fType & MF_SYSMENU)
1420 if( !IsIconic(hwnd) )
1421 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1422 return;
1425 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1426 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1428 /* Setup colors */
1430 if (lpitem->fState & MF_HILITE)
1432 if(menuBar && !flat_menu) {
1433 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1434 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1435 } else {
1436 if(lpitem->fState & MF_GRAYED)
1437 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1438 else
1439 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1440 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1443 else
1445 if (lpitem->fState & MF_GRAYED)
1446 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1447 else
1448 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1449 SetBkColor( hdc, GetSysColor( bkgnd ) );
1452 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1453 rect = lpitem->rect;
1454 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1456 if (lpitem->fType & MF_OWNERDRAW)
1459 ** Experimentation under Windows reveals that an owner-drawn
1460 ** menu is given the rectangle which includes the space it requested
1461 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1462 ** and a popup-menu arrow. This is the value of lpitem->rect.
1463 ** Windows will leave all drawing to the application except for
1464 ** the popup-menu arrow. Windows always draws that itself, after
1465 ** the menu owner has finished drawing.
1467 DRAWITEMSTRUCT dis;
1469 dis.CtlType = ODT_MENU;
1470 dis.CtlID = 0;
1471 dis.itemID = lpitem->wID;
1472 dis.itemData = lpitem->dwItemData;
1473 dis.itemState = 0;
1474 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1475 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1476 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1477 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1478 dis.hwndItem = (HWND)hmenu;
1479 dis.hDC = hdc;
1480 dis.rcItem = rect;
1481 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1482 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1483 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1484 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1485 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1486 /* Draw the popup-menu arrow */
1487 if (lpitem->fType & MF_POPUP)
1488 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1489 arrow_bitmap_height);
1490 return;
1493 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1495 if (lpitem->fState & MF_HILITE)
1497 if (flat_menu)
1499 InflateRect (&rect, -1, -1);
1500 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1501 InflateRect (&rect, 1, 1);
1502 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1504 else
1506 if(menuBar)
1507 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1508 else
1509 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1512 else
1513 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1515 SetBkMode( hdc, TRANSPARENT );
1517 /* vertical separator */
1518 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1520 HPEN oldPen;
1521 RECT rc = rect;
1523 rc.left -= MENU_COL_SPACE / 2 + 1;
1524 rc.top = 3;
1525 rc.bottom = height - 3;
1526 if (flat_menu)
1528 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1529 MoveToEx( hdc, rc.left, rc.top, NULL );
1530 LineTo( hdc, rc.left, rc.bottom );
1531 SelectObject( hdc, oldPen );
1533 else
1534 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1537 /* horizontal separator */
1538 if (lpitem->fType & MF_SEPARATOR)
1540 HPEN oldPen;
1541 RECT rc = rect;
1543 rc.left++;
1544 rc.right--;
1545 rc.top = ( rc.top + rc.bottom) / 2;
1546 if (flat_menu)
1548 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1549 MoveToEx( hdc, rc.left, rc.top, NULL );
1550 LineTo( hdc, rc.right, rc.top );
1551 SelectObject( hdc, oldPen );
1553 else
1554 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1555 return;
1558 /* helper lines for debugging */
1559 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1560 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1561 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1562 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1565 if (lpitem->hbmpItem) {
1566 /* calculate the bitmap rectangle in coordinates relative
1567 * to the item rectangle */
1568 if( menuBar) {
1569 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1570 bmprc.left = 3;
1571 else
1572 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1574 else if (menu->dwStyle & MNS_NOCHECK)
1575 bmprc.left = 4;
1576 else if (menu->dwStyle & MNS_CHECKORBMP)
1577 bmprc.left = 2;
1578 else
1579 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1580 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1581 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1582 bmprc.top = 0;
1583 else
1584 bmprc.top = (rect.bottom - rect.top -
1585 lpitem->bmpsize.cy) / 2;
1586 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1589 if (!menuBar)
1591 HBITMAP bm;
1592 INT y = rect.top + rect.bottom;
1593 RECT rc = rect;
1594 BOOL checked = FALSE;
1595 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1596 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1597 /* Draw the check mark
1599 * FIXME:
1600 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1602 if( !(menu->dwStyle & MNS_NOCHECK)) {
1603 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1604 lpitem->hUnCheckBit;
1605 if (bm) /* we have a custom bitmap */
1607 HDC hdcMem = CreateCompatibleDC( hdc );
1609 SelectObject( hdcMem, bm );
1610 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1611 check_bitmap_width, check_bitmap_height,
1612 hdcMem, 0, 0, SRCCOPY );
1613 DeleteDC( hdcMem );
1614 checked = TRUE;
1616 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1618 RECT r;
1619 HBITMAP bm = CreateBitmap( check_bitmap_width,
1620 check_bitmap_height, 1, 1, NULL );
1621 HDC hdcMem = CreateCompatibleDC( hdc );
1623 SelectObject( hdcMem, bm );
1624 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1625 DrawFrameControl( hdcMem, &r, DFC_MENU,
1626 (lpitem->fType & MFT_RADIOCHECK) ?
1627 DFCS_MENUBULLET : DFCS_MENUCHECK );
1628 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1629 hdcMem, 0, 0, SRCCOPY );
1630 DeleteDC( hdcMem );
1631 DeleteObject( bm );
1632 checked = TRUE;
1635 if( lpitem->hbmpItem &&
1636 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1637 POINT origorg;
1638 /* some applications make this assumption on the DC's origin */
1639 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1640 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1641 odaction, FALSE);
1642 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1644 /* Draw the popup-menu arrow */
1645 if (lpitem->fType & MF_POPUP)
1646 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1647 arrow_bitmap_height);
1648 rect.left += 4;
1649 if( !(menu->dwStyle & MNS_NOCHECK))
1650 rect.left += check_bitmap_width;
1651 rect.right -= arrow_bitmap_width;
1653 else if( lpitem->hbmpItem)
1654 { /* Draw the bitmap */
1655 POINT origorg;
1657 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1658 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1659 odaction, menuBar);
1660 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1662 /* process text if present */
1663 if (lpitem->text)
1665 int i;
1666 HFONT hfontOld = 0;
1668 UINT uFormat = (menuBar) ?
1669 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1670 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1672 if( !(menu->dwStyle & MNS_CHECKORBMP))
1673 rect.left += menu->textOffset;
1675 if ( lpitem->fState & MFS_DEFAULT )
1677 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1680 if (menuBar) {
1681 if( lpitem->hbmpItem)
1682 rect.left += lpitem->bmpsize.cx;
1683 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1684 rect.left += menucharsize.cx;
1685 rect.right -= menucharsize.cx;
1688 for (i = 0; lpitem->text[i]; i++)
1689 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1690 break;
1692 if(lpitem->fState & MF_GRAYED)
1694 if (!(lpitem->fState & MF_HILITE) )
1696 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1697 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1698 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1699 --rect.left; --rect.top; --rect.right; --rect.bottom;
1701 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1704 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1706 /* paint the shortcut text */
1707 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1709 if (lpitem->text[i] == '\t')
1711 rect.left = lpitem->xTab;
1712 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1714 else
1716 rect.right = lpitem->xTab;
1717 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1720 if(lpitem->fState & MF_GRAYED)
1722 if (!(lpitem->fState & MF_HILITE) )
1724 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1725 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1726 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1727 --rect.left; --rect.top; --rect.right; --rect.bottom;
1729 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1731 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1734 if (hfontOld)
1735 SelectObject (hdc, hfontOld);
1740 /***********************************************************************
1741 * MENU_DrawPopupMenu
1743 * Paint a popup menu.
1745 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1747 HBRUSH hPrevBrush = 0;
1748 RECT rect;
1750 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1752 GetClientRect( hwnd, &rect );
1754 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1755 && (SelectObject( hdc, get_menu_font(FALSE))))
1757 HPEN hPrevPen;
1759 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1761 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1762 if( hPrevPen )
1764 POPUPMENU *menu;
1765 BOOL flat_menu = FALSE;
1767 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1768 if (flat_menu)
1769 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1770 else
1771 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1773 if( (menu = MENU_GetMenu( hmenu )))
1775 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1776 /* draw menu items */
1777 if( menu->nItems)
1779 MENUITEM *item;
1780 UINT u;
1782 item = menu->items;
1783 for( u = menu->nItems; u > 0; u--, item++)
1784 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1785 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1787 /* draw scroll arrows */
1788 if (menu->bScrolling)
1789 MENU_DrawScrollArrows(menu, hdc);
1791 } else
1793 SelectObject( hdc, hPrevBrush );
1798 /***********************************************************************
1799 * MENU_DrawMenuBar
1801 * Paint a menu bar. Returns the height of the menu bar.
1802 * called from [windows/nonclient.c]
1804 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1806 LPPOPUPMENU lppop;
1807 HMENU hMenu = GetMenu(hwnd);
1809 lppop = MENU_GetMenu( hMenu );
1810 if (lppop == NULL || lprect == NULL)
1812 return GetSystemMetrics(SM_CYMENU);
1815 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1819 /***********************************************************************
1820 * MENU_InitPopup
1822 * Popup menu initialization before WM_ENTERMENULOOP.
1824 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1826 POPUPMENU *menu;
1827 DWORD ex_style = 0;
1829 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1831 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1833 /* store the owner for DrawItem */
1834 if (!IsWindow( hwndOwner ))
1836 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1837 return FALSE;
1839 menu->hwndOwner = hwndOwner;
1841 if (flags & TPM_LAYOUTRTL)
1842 ex_style = WS_EX_LAYOUTRTL;
1844 /* NOTE: In Windows, top menu popup is not owned. */
1845 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1846 WS_POPUP, 0, 0, 0, 0,
1847 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1848 (LPVOID)hmenu );
1849 if( !menu->hWnd ) return FALSE;
1850 return TRUE;
1854 /***********************************************************************
1855 * MENU_ShowPopup
1857 * Display a popup menu.
1859 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1860 INT x, INT y, INT xanchor, INT yanchor )
1862 POPUPMENU *menu;
1863 INT width, height;
1864 POINT pt;
1865 HMONITOR monitor;
1866 MONITORINFO info;
1868 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1869 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1871 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1872 if (menu->FocusedItem != NO_SELECTED_ITEM)
1874 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1875 menu->FocusedItem = NO_SELECTED_ITEM;
1878 menu->nScrollPos = 0;
1879 MENU_PopupMenuCalcSize( menu );
1881 /* adjust popup menu pos so that it fits within the desktop */
1883 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1884 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1886 /* FIXME: should use item rect */
1887 pt.x = x;
1888 pt.y = y;
1889 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1890 info.cbSize = sizeof(info);
1891 GetMonitorInfoW( monitor, &info );
1893 if (flags & TPM_LAYOUTRTL)
1894 flags ^= TPM_RIGHTALIGN;
1896 if( flags & TPM_RIGHTALIGN ) x -= width;
1897 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1899 if( flags & TPM_BOTTOMALIGN ) y -= height;
1900 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1902 if( x + width > info.rcWork.right)
1904 if( xanchor && x >= width - xanchor )
1905 x -= width - xanchor;
1907 if( x + width > info.rcWork.right)
1908 x = info.rcWork.right - width;
1910 if( x < info.rcWork.left ) x = info.rcWork.left;
1912 if( y + height > info.rcWork.bottom)
1914 if( yanchor && y >= height + yanchor )
1915 y -= height + yanchor;
1917 if( y + height > info.rcWork.bottom)
1918 y = info.rcWork.bottom - height;
1920 if( y < info.rcWork.top ) y = info.rcWork.top;
1922 if (!top_popup) {
1923 top_popup = menu->hWnd;
1924 top_popup_hmenu = hmenu;
1926 /* Display the window */
1928 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, width, height,
1929 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1930 UpdateWindow( menu->hWnd );
1931 return TRUE;
1935 /***********************************************************************
1936 * MENU_EnsureMenuItemVisible
1938 static void
1939 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1941 if (lppop->bScrolling)
1943 MENUITEM *item = &lppop->items[wIndex];
1944 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1945 UINT nOldPos = lppop->nScrollPos;
1946 RECT rc;
1947 UINT arrow_bitmap_height;
1948 BITMAP bmp;
1950 GetClientRect(lppop->hWnd, &rc);
1952 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1953 arrow_bitmap_height = bmp.bmHeight;
1955 rc.top += arrow_bitmap_height;
1956 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1958 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1959 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1962 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1963 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1964 MENU_DrawScrollArrows(lppop, hdc);
1966 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1968 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1969 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1970 MENU_DrawScrollArrows(lppop, hdc);
1976 /***********************************************************************
1977 * MENU_SelectItem
1979 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1980 BOOL sendMenuSelect, HMENU topmenu )
1982 LPPOPUPMENU lppop;
1983 HDC hdc;
1985 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1987 lppop = MENU_GetMenu( hmenu );
1988 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1990 if (lppop->FocusedItem == wIndex) return;
1991 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1992 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1993 if (!top_popup) {
1994 top_popup = lppop->hWnd;
1995 top_popup_hmenu = hmenu;
1998 SelectObject( hdc, get_menu_font(FALSE));
2000 /* Clear previous highlighted item */
2001 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2003 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2004 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2005 lppop->Height, !(lppop->wFlags & MF_POPUP),
2006 ODA_SELECT );
2009 /* Highlight new item (if any) */
2010 lppop->FocusedItem = wIndex;
2011 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2013 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2014 lppop->items[wIndex].fState |= MF_HILITE;
2015 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2016 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2017 &lppop->items[wIndex], lppop->Height,
2018 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2020 if (sendMenuSelect)
2022 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2023 SendMessageW( hwndOwner, WM_MENUSELECT,
2024 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2025 ip->fType | ip->fState |
2026 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2029 else if (sendMenuSelect) {
2030 if(topmenu){
2031 int pos;
2032 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2033 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2034 MENUITEM *ip = &ptm->items[pos];
2035 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2036 ip->fType | ip->fState |
2037 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2041 ReleaseDC( lppop->hWnd, hdc );
2045 /***********************************************************************
2046 * MENU_MoveSelection
2048 * Moves currently selected item according to the offset parameter.
2049 * If there is no selection then it should select the last item if
2050 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2052 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2054 INT i;
2055 POPUPMENU *menu;
2057 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2059 menu = MENU_GetMenu( hmenu );
2060 if ((!menu) || (!menu->items)) return;
2062 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2064 if( menu->nItems == 1 ) return; else
2065 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2066 ; i += offset)
2067 if (!(menu->items[i].fType & MF_SEPARATOR))
2069 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2070 return;
2074 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2075 i >= 0 && i < menu->nItems ; i += offset)
2076 if (!(menu->items[i].fType & MF_SEPARATOR))
2078 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2079 return;
2084 /**********************************************************************
2085 * MENU_InsertItem
2087 * Insert (allocate) a new item into a menu.
2089 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2091 MENUITEM *newItems;
2092 POPUPMENU *menu;
2094 if (!(menu = MENU_GetMenu(hMenu)))
2095 return NULL;
2097 /* Find where to insert new item */
2099 if (flags & MF_BYPOSITION) {
2100 if (pos > menu->nItems)
2101 pos = menu->nItems;
2102 } else {
2103 if (!MENU_FindItem( &hMenu, &pos, flags ))
2104 pos = menu->nItems;
2105 else {
2106 if (!(menu = MENU_GetMenu( hMenu )))
2107 return NULL;
2111 /* Make sure that MDI system buttons stay on the right side.
2112 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2113 * regardless of their id.
2115 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2116 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2117 pos--;
2119 TRACE("inserting at %u flags %x\n", pos, flags);
2121 /* Create new items array */
2123 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2124 if (!newItems)
2126 WARN("allocation failed\n" );
2127 return NULL;
2129 if (menu->nItems > 0)
2131 /* Copy the old array into the new one */
2132 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2133 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2134 (menu->nItems-pos)*sizeof(MENUITEM) );
2135 HeapFree( GetProcessHeap(), 0, menu->items );
2137 menu->items = newItems;
2138 menu->nItems++;
2139 memset( &newItems[pos], 0, sizeof(*newItems) );
2140 menu->Height = 0; /* force size recalculate */
2141 return &newItems[pos];
2145 /**********************************************************************
2146 * MENU_ParseResource
2148 * Parse a standard menu resource and add items to the menu.
2149 * Return a pointer to the end of the resource.
2151 * NOTE: flags is equivalent to the mtOption field
2153 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2155 WORD flags, id = 0;
2156 LPCWSTR str;
2157 BOOL end_flag;
2161 flags = GET_WORD(res);
2162 end_flag = flags & MF_END;
2163 /* Remove MF_END because it has the same value as MF_HILITE */
2164 flags &= ~MF_END;
2165 res += sizeof(WORD);
2166 if (!(flags & MF_POPUP))
2168 id = GET_WORD(res);
2169 res += sizeof(WORD);
2171 str = (LPCWSTR)res;
2172 res += (strlenW(str) + 1) * sizeof(WCHAR);
2173 if (flags & MF_POPUP)
2175 HMENU hSubMenu = CreatePopupMenu();
2176 if (!hSubMenu) return NULL;
2177 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2178 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2180 else /* Not a popup */
2182 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2184 } while (!end_flag);
2185 return res;
2189 /**********************************************************************
2190 * MENUEX_ParseResource
2192 * Parse an extended menu resource and add items to the menu.
2193 * Return a pointer to the end of the resource.
2195 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2197 WORD resinfo;
2198 do {
2199 MENUITEMINFOW mii;
2201 mii.cbSize = sizeof(mii);
2202 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2203 mii.fType = GET_DWORD(res);
2204 res += sizeof(DWORD);
2205 mii.fState = GET_DWORD(res);
2206 res += sizeof(DWORD);
2207 mii.wID = GET_DWORD(res);
2208 res += sizeof(DWORD);
2209 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2210 res += sizeof(WORD);
2211 /* Align the text on a word boundary. */
2212 res += (~((UINT_PTR)res - 1)) & 1;
2213 mii.dwTypeData = (LPWSTR) res;
2214 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2215 /* Align the following fields on a dword boundary. */
2216 res += (~((UINT_PTR)res - 1)) & 3;
2218 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2219 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2221 if (resinfo & 1) { /* Pop-up? */
2222 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2223 res += sizeof(DWORD);
2224 mii.hSubMenu = CreatePopupMenu();
2225 if (!mii.hSubMenu)
2226 return NULL;
2227 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2228 DestroyMenu(mii.hSubMenu);
2229 return NULL;
2231 mii.fMask |= MIIM_SUBMENU;
2232 mii.fType |= MF_POPUP;
2234 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2236 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2237 mii.wID, mii.fType);
2238 mii.fType |= MF_SEPARATOR;
2240 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2241 } while (!(resinfo & MF_END));
2242 return res;
2246 /***********************************************************************
2247 * MENU_GetSubPopup
2249 * Return the handle of the selected sub-popup menu (if any).
2251 static HMENU MENU_GetSubPopup( HMENU hmenu )
2253 POPUPMENU *menu;
2254 MENUITEM *item;
2256 menu = MENU_GetMenu( hmenu );
2258 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2260 item = &menu->items[menu->FocusedItem];
2261 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2262 return item->hSubMenu;
2263 return 0;
2267 /***********************************************************************
2268 * MENU_HideSubPopups
2270 * Hide the sub-popup menus of this menu.
2272 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2273 BOOL sendMenuSelect, UINT wFlags )
2275 POPUPMENU *menu = MENU_GetMenu( hmenu );
2277 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2279 if (menu && top_popup)
2281 HMENU hsubmenu;
2282 POPUPMENU *submenu;
2283 MENUITEM *item;
2285 if (menu->FocusedItem != NO_SELECTED_ITEM)
2287 item = &menu->items[menu->FocusedItem];
2288 if (!(item->fType & MF_POPUP) ||
2289 !(item->fState & MF_MOUSESELECT)) return;
2290 item->fState &= ~MF_MOUSESELECT;
2291 hsubmenu = item->hSubMenu;
2292 } else return;
2294 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2295 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2296 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2297 DestroyWindow( submenu->hWnd );
2298 submenu->hWnd = 0;
2300 if (!(wFlags & TPM_NONOTIFY))
2301 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2302 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2307 /***********************************************************************
2308 * MENU_ShowSubPopup
2310 * Display the sub-menu of the selected item of this menu.
2311 * Return the handle of the submenu, or hmenu if no submenu to display.
2313 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2314 BOOL selectFirst, UINT wFlags )
2316 RECT rect;
2317 POPUPMENU *menu;
2318 MENUITEM *item;
2319 HDC hdc;
2321 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2323 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2325 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2327 item = &menu->items[menu->FocusedItem];
2328 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2329 return hmenu;
2331 /* message must be sent before using item,
2332 because nearly everything may be changed by the application ! */
2334 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2335 if (!(wFlags & TPM_NONOTIFY))
2336 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2337 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2339 item = &menu->items[menu->FocusedItem];
2340 rect = item->rect;
2342 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2343 if (!(item->fState & MF_HILITE))
2345 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2346 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2348 SelectObject( hdc, get_menu_font(FALSE));
2350 item->fState |= MF_HILITE;
2351 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2352 ReleaseDC( menu->hWnd, hdc );
2354 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2355 item->rect = rect;
2357 item->fState |= MF_MOUSESELECT;
2359 if (IS_SYSTEM_MENU(menu))
2361 MENU_InitSysMenuPopup(item->hSubMenu,
2362 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2363 GetClassLongW( menu->hWnd, GCL_STYLE));
2365 NC_GetSysPopupPos( menu->hWnd, &rect );
2366 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2367 rect.top = rect.bottom;
2368 rect.right = GetSystemMetrics(SM_CXSIZE);
2369 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2371 else
2373 GetWindowRect( menu->hWnd, &rect );
2374 if (menu->wFlags & MF_POPUP)
2376 RECT rc = item->rect;
2378 MENU_AdjustMenuItemRect(menu, &rc);
2380 /* The first item in the popup menu has to be at the
2381 same y position as the focused menu item */
2382 if (wFlags & TPM_LAYOUTRTL)
2383 rect.left += GetSystemMetrics(SM_CXBORDER);
2384 else
2385 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2386 rect.top += rc.top - MENU_TOP_MARGIN;
2387 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2388 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2389 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2391 else
2393 if (wFlags & TPM_LAYOUTRTL)
2394 rect.left = rect.right - item->rect.left;
2395 else
2396 rect.left += item->rect.left;
2397 rect.top += item->rect.bottom;
2398 rect.right = item->rect.right - item->rect.left;
2399 rect.bottom = item->rect.bottom - item->rect.top;
2403 /* use default alignment for submenus */
2404 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2406 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2408 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2409 rect.left, rect.top, rect.right, rect.bottom );
2410 if (selectFirst)
2411 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2412 return item->hSubMenu;
2417 /**********************************************************************
2418 * MENU_IsMenuActive
2420 HWND MENU_IsMenuActive(void)
2422 return top_popup;
2425 /**********************************************************************
2426 * MENU_EndMenu
2428 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2430 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2432 void MENU_EndMenu( HWND hwnd )
2434 POPUPMENU *menu;
2435 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2436 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2439 /***********************************************************************
2440 * MENU_PtMenu
2442 * Walks menu chain trying to find a menu pt maps to.
2444 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2446 POPUPMENU *menu = MENU_GetMenu( hMenu );
2447 UINT item = menu->FocusedItem;
2448 HMENU ret;
2450 /* try subpopup first (if any) */
2451 ret = (item != NO_SELECTED_ITEM &&
2452 (menu->items[item].fType & MF_POPUP) &&
2453 (menu->items[item].fState & MF_MOUSESELECT))
2454 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2456 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2458 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2459 if( menu->wFlags & MF_POPUP )
2461 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2463 else if (ht == HTSYSMENU)
2464 ret = get_win_sys_menu( menu->hWnd );
2465 else if (ht == HTMENU)
2466 ret = GetMenu( menu->hWnd );
2468 return ret;
2471 /***********************************************************************
2472 * MENU_ExecFocusedItem
2474 * Execute a menu item (for instance when user pressed Enter).
2475 * Return the wID of the executed item. Otherwise, -1 indicating
2476 * that no menu item was executed, -2 if a popup is shown;
2477 * Have to receive the flags for the TrackPopupMenu options to avoid
2478 * sending unwanted message.
2481 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2483 MENUITEM *item;
2484 POPUPMENU *menu = MENU_GetMenu( hMenu );
2486 TRACE("%p hmenu=%p\n", pmt, hMenu);
2488 if (!menu || !menu->nItems ||
2489 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2491 item = &menu->items[menu->FocusedItem];
2493 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2495 if (!(item->fType & MF_POPUP))
2497 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2499 /* If TPM_RETURNCMD is set you return the id, but
2500 do not send a message to the owner */
2501 if(!(wFlags & TPM_RETURNCMD))
2503 if( menu->wFlags & MF_SYSMENU )
2504 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2505 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2506 else
2508 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2509 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2511 if (dwStyle & MNS_NOTIFYBYPOS)
2512 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2513 (LPARAM)hMenu);
2514 else
2515 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2518 return item->wID;
2521 else
2523 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2524 return -2;
2527 return -1;
2530 /***********************************************************************
2531 * MENU_SwitchTracking
2533 * Helper function for menu navigation routines.
2535 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2537 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2538 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2540 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2542 if( pmt->hTopMenu != hPtMenu &&
2543 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2545 /* both are top level menus (system and menu-bar) */
2546 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2547 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2548 pmt->hTopMenu = hPtMenu;
2550 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2551 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2555 /***********************************************************************
2556 * MENU_ButtonDown
2558 * Return TRUE if we can go on with menu tracking.
2560 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2562 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2564 if (hPtMenu)
2566 UINT id = 0;
2567 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2568 MENUITEM *item;
2570 if( IS_SYSTEM_MENU(ptmenu) )
2571 item = ptmenu->items;
2572 else
2573 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2575 if( item )
2577 if( ptmenu->FocusedItem != id )
2578 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2580 /* If the popup menu is not already "popped" */
2581 if(!(item->fState & MF_MOUSESELECT ))
2583 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2586 return TRUE;
2588 /* Else the click was on the menu bar, finish the tracking */
2590 return FALSE;
2593 /***********************************************************************
2594 * MENU_ButtonUp
2596 * Return the value of MENU_ExecFocusedItem if
2597 * the selected item was not a popup. Else open the popup.
2598 * A -1 return value indicates that we go on with menu tracking.
2601 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2603 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2605 if (hPtMenu)
2607 UINT id = 0;
2608 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2609 MENUITEM *item;
2611 if( IS_SYSTEM_MENU(ptmenu) )
2612 item = ptmenu->items;
2613 else
2614 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2616 if( item && (ptmenu->FocusedItem == id ))
2618 debug_print_menuitem ("FocusedItem: ", item, "");
2620 if( !(item->fType & MF_POPUP) )
2622 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2623 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2624 return executedMenuId;
2627 /* If we are dealing with the top-level menu */
2628 /* and this is a click on an already "popped" item: */
2629 /* Stop the menu tracking and close the opened submenus */
2630 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2631 return 0;
2633 ptmenu->bTimeToHide = TRUE;
2635 return -1;
2639 /***********************************************************************
2640 * MENU_MouseMove
2642 * Return TRUE if we can go on with menu tracking.
2644 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2646 UINT id = NO_SELECTED_ITEM;
2647 POPUPMENU *ptmenu = NULL;
2649 if( hPtMenu )
2651 ptmenu = MENU_GetMenu( hPtMenu );
2652 if( IS_SYSTEM_MENU(ptmenu) )
2653 id = 0;
2654 else
2655 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2658 if( id == NO_SELECTED_ITEM )
2660 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2661 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2664 else if( ptmenu->FocusedItem != id )
2666 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2667 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2669 return TRUE;
2673 /***********************************************************************
2674 * MENU_DoNextMenu
2676 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2678 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2680 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2681 BOOL atEnd = FALSE;
2683 /* When skipping left, we need to do something special after the
2684 first menu. */
2685 if (vk == VK_LEFT && menu->FocusedItem == 0)
2687 atEnd = TRUE;
2689 /* When skipping right, for the non-system menu, we need to
2690 handle the last non-special menu item (ie skip any window
2691 icons such as MDI maximize, restore or close) */
2692 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2694 UINT i = menu->FocusedItem + 1;
2695 while (i < menu->nItems) {
2696 if ((menu->items[i].wID >= SC_SIZE &&
2697 menu->items[i].wID <= SC_RESTORE)) {
2698 i++;
2699 } else break;
2701 if (i == menu->nItems) {
2702 atEnd = TRUE;
2705 /* When skipping right, we need to cater for the system menu */
2706 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2708 if (menu->FocusedItem == (menu->nItems - 1)) {
2709 atEnd = TRUE;
2713 if( atEnd )
2715 MDINEXTMENU next_menu;
2716 HMENU hNewMenu;
2717 HWND hNewWnd;
2718 UINT id = 0;
2720 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2721 next_menu.hmenuNext = 0;
2722 next_menu.hwndNext = 0;
2723 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2725 TRACE("%p [%p] -> %p [%p]\n",
2726 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2728 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2730 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2731 hNewWnd = pmt->hOwnerWnd;
2732 if( IS_SYSTEM_MENU(menu) )
2734 /* switch to the menu bar */
2736 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2738 if( vk == VK_LEFT )
2740 menu = MENU_GetMenu( hNewMenu );
2741 id = menu->nItems - 1;
2743 /* Skip backwards over any system predefined icons,
2744 eg. MDI close, restore etc icons */
2745 while ((id > 0) &&
2746 (menu->items[id].wID >= SC_SIZE &&
2747 menu->items[id].wID <= SC_RESTORE)) id--;
2750 else if (style & WS_SYSMENU )
2752 /* switch to the system menu */
2753 hNewMenu = get_win_sys_menu( hNewWnd );
2755 else return FALSE;
2757 else /* application returned a new menu to switch to */
2759 hNewMenu = next_menu.hmenuNext;
2760 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2762 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2764 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2766 if (style & WS_SYSMENU &&
2767 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2769 /* get the real system menu */
2770 hNewMenu = get_win_sys_menu(hNewWnd);
2772 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2774 /* FIXME: Not sure what to do here;
2775 * perhaps try to track hNewMenu as a popup? */
2777 TRACE(" -- got confused.\n");
2778 return FALSE;
2781 else return FALSE;
2784 if( hNewMenu != pmt->hTopMenu )
2786 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2787 FALSE, 0 );
2788 if( pmt->hCurrentMenu != pmt->hTopMenu )
2789 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2792 if( hNewWnd != pmt->hOwnerWnd )
2794 pmt->hOwnerWnd = hNewWnd;
2795 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2798 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2799 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2801 return TRUE;
2803 return FALSE;
2806 /***********************************************************************
2807 * MENU_SuspendPopup
2809 * The idea is not to show the popup if the next input message is
2810 * going to hide it anyway.
2812 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2814 MSG msg;
2816 msg.hwnd = pmt->hOwnerWnd;
2818 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2819 pmt->trackFlags |= TF_SKIPREMOVE;
2821 switch( uMsg )
2823 case WM_KEYDOWN:
2824 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2825 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2827 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2828 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2829 if( msg.message == WM_KEYDOWN &&
2830 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2832 pmt->trackFlags |= TF_SUSPENDPOPUP;
2833 return TRUE;
2836 break;
2839 /* failures go through this */
2840 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2841 return FALSE;
2844 /***********************************************************************
2845 * MENU_KeyEscape
2847 * Handle a VK_ESCAPE key event in a menu.
2849 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2851 BOOL bEndMenu = TRUE;
2853 if (pmt->hCurrentMenu != pmt->hTopMenu)
2855 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2857 if (menu->wFlags & MF_POPUP)
2859 HMENU hmenutmp, hmenuprev;
2861 hmenuprev = hmenutmp = pmt->hTopMenu;
2863 /* close topmost popup */
2864 while (hmenutmp != pmt->hCurrentMenu)
2866 hmenuprev = hmenutmp;
2867 hmenutmp = MENU_GetSubPopup( hmenuprev );
2870 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2871 pmt->hCurrentMenu = hmenuprev;
2872 bEndMenu = FALSE;
2876 return bEndMenu;
2879 /***********************************************************************
2880 * MENU_KeyLeft
2882 * Handle a VK_LEFT key event in a menu.
2884 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2886 POPUPMENU *menu;
2887 HMENU hmenutmp, hmenuprev;
2888 UINT prevcol;
2890 hmenuprev = hmenutmp = pmt->hTopMenu;
2891 menu = MENU_GetMenu( hmenutmp );
2893 /* Try to move 1 column left (if possible) */
2894 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2895 NO_SELECTED_ITEM ) {
2897 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2898 prevcol, TRUE, 0 );
2899 return;
2902 /* close topmost popup */
2903 while (hmenutmp != pmt->hCurrentMenu)
2905 hmenuprev = hmenutmp;
2906 hmenutmp = MENU_GetSubPopup( hmenuprev );
2909 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2910 pmt->hCurrentMenu = hmenuprev;
2912 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2914 /* move menu bar selection if no more popups are left */
2916 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2917 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2919 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2921 /* A sublevel menu was displayed - display the next one
2922 * unless there is another displacement coming up */
2924 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2925 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2926 pmt->hTopMenu, TRUE, wFlags);
2932 /***********************************************************************
2933 * MENU_KeyRight
2935 * Handle a VK_RIGHT key event in a menu.
2937 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2939 HMENU hmenutmp;
2940 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2941 UINT nextcol;
2943 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2944 pmt->hCurrentMenu,
2945 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2946 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2948 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2950 /* If already displaying a popup, try to display sub-popup */
2952 hmenutmp = pmt->hCurrentMenu;
2953 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2955 /* if subpopup was displayed then we are done */
2956 if (hmenutmp != pmt->hCurrentMenu) return;
2959 /* Check to see if there's another column */
2960 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2961 NO_SELECTED_ITEM ) {
2962 TRACE("Going to %d.\n", nextcol );
2963 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2964 nextcol, TRUE, 0 );
2965 return;
2968 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2970 if( pmt->hCurrentMenu != pmt->hTopMenu )
2972 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2973 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2974 } else hmenutmp = 0;
2976 /* try to move to the next item */
2977 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2978 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2980 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2981 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2982 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2983 pmt->hTopMenu, TRUE, wFlags);
2987 static void CALLBACK release_capture( BOOL __normal )
2989 set_capture_window( 0, GUI_INMENUMODE, NULL );
2992 /***********************************************************************
2993 * MENU_TrackMenu
2995 * Menu tracking code.
2997 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2998 HWND hwnd, const RECT *lprect )
3000 MSG msg;
3001 POPUPMENU *menu;
3002 BOOL fRemove;
3003 INT executedMenuId = -1;
3004 MTRACKER mt;
3005 BOOL enterIdleSent = FALSE;
3006 HWND capture_win;
3008 mt.trackFlags = 0;
3009 mt.hCurrentMenu = hmenu;
3010 mt.hTopMenu = hmenu;
3011 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3012 mt.pt.x = x;
3013 mt.pt.y = y;
3015 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3016 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3018 fEndMenu = FALSE;
3019 if (!(menu = MENU_GetMenu( hmenu )))
3021 WARN("Invalid menu handle %p\n", hmenu);
3022 SetLastError(ERROR_INVALID_MENU_HANDLE);
3023 return FALSE;
3026 if (wFlags & TPM_BUTTONDOWN)
3028 /* Get the result in order to start the tracking or not */
3029 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3030 fEndMenu = !fRemove;
3033 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3035 /* owner may not be visible when tracking a popup, so use the menu itself */
3036 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3037 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3039 __TRY while (!fEndMenu)
3041 menu = MENU_GetMenu( mt.hCurrentMenu );
3042 if (!menu) /* sometimes happens if I do a window manager close */
3043 break;
3045 /* we have to keep the message in the queue until it's
3046 * clear that menu loop is not over yet. */
3048 for (;;)
3050 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3052 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3053 /* remove the message from the queue */
3054 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3056 else
3058 if (!enterIdleSent)
3060 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3061 enterIdleSent = TRUE;
3062 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3064 WaitMessage();
3068 /* check if EndMenu() tried to cancel us, by posting this message */
3069 if(msg.message == WM_CANCELMODE)
3071 /* we are now out of the loop */
3072 fEndMenu = TRUE;
3074 /* remove the message from the queue */
3075 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3077 /* break out of internal loop, ala ESCAPE */
3078 break;
3081 TranslateMessage( &msg );
3082 mt.pt = msg.pt;
3084 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3085 enterIdleSent=FALSE;
3087 fRemove = FALSE;
3088 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3091 * Use the mouse coordinates in lParam instead of those in the MSG
3092 * struct to properly handle synthetic messages. They are already
3093 * in screen coordinates.
3095 mt.pt.x = (short)LOWORD(msg.lParam);
3096 mt.pt.y = (short)HIWORD(msg.lParam);
3098 /* Find a menu for this mouse event */
3099 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3101 switch(msg.message)
3103 /* no WM_NC... messages in captured state */
3105 case WM_RBUTTONDBLCLK:
3106 case WM_RBUTTONDOWN:
3107 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3108 /* fall through */
3109 case WM_LBUTTONDBLCLK:
3110 case WM_LBUTTONDOWN:
3111 /* If the message belongs to the menu, removes it from the queue */
3112 /* Else, end menu tracking */
3113 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3114 fEndMenu = !fRemove;
3115 break;
3117 case WM_RBUTTONUP:
3118 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3119 /* fall through */
3120 case WM_LBUTTONUP:
3121 /* Check if a menu was selected by the mouse */
3122 if (hmenu)
3124 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3125 TRACE("executedMenuId %d\n", executedMenuId);
3127 /* End the loop if executedMenuId is an item ID */
3128 /* or if the job was done (executedMenuId = 0). */
3129 fEndMenu = fRemove = (executedMenuId != -1);
3131 /* No menu was selected by the mouse */
3132 /* if the function was called by TrackPopupMenu, continue
3133 with the menu tracking. If not, stop it */
3134 else
3135 fEndMenu = !(wFlags & TPM_POPUPMENU);
3137 break;
3139 case WM_MOUSEMOVE:
3140 /* the selected menu item must be changed every time */
3141 /* the mouse moves. */
3143 if (hmenu)
3144 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3146 } /* switch(msg.message) - mouse */
3148 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3150 fRemove = TRUE; /* Keyboard messages are always removed */
3151 switch(msg.message)
3153 case WM_KEYDOWN:
3154 case WM_SYSKEYDOWN:
3155 switch(msg.wParam)
3157 case VK_MENU:
3158 case VK_F10:
3159 fEndMenu = TRUE;
3160 break;
3162 case VK_HOME:
3163 case VK_END:
3164 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3165 NO_SELECTED_ITEM, FALSE, 0 );
3166 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3167 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3168 break;
3170 case VK_UP:
3171 case VK_DOWN: /* If on menu bar, pull-down the menu */
3173 menu = MENU_GetMenu( mt.hCurrentMenu );
3174 if (!(menu->wFlags & MF_POPUP))
3175 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3176 else /* otherwise try to move selection */
3177 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3178 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3179 break;
3181 case VK_LEFT:
3182 MENU_KeyLeft( &mt, wFlags );
3183 break;
3185 case VK_RIGHT:
3186 MENU_KeyRight( &mt, wFlags );
3187 break;
3189 case VK_ESCAPE:
3190 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3191 break;
3193 case VK_F1:
3195 HELPINFO hi;
3196 hi.cbSize = sizeof(HELPINFO);
3197 hi.iContextType = HELPINFO_MENUITEM;
3198 if (menu->FocusedItem == NO_SELECTED_ITEM)
3199 hi.iCtrlId = 0;
3200 else
3201 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3202 hi.hItemHandle = hmenu;
3203 hi.dwContextId = menu->dwContextHelpID;
3204 hi.MousePos = msg.pt;
3205 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3206 break;
3209 default:
3210 break;
3212 break; /* WM_KEYDOWN */
3214 case WM_CHAR:
3215 case WM_SYSCHAR:
3217 UINT pos;
3219 if (msg.wParam == '\r' || msg.wParam == ' ')
3221 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3222 fEndMenu = (executedMenuId != -2);
3224 break;
3227 /* Hack to avoid control chars. */
3228 /* We will find a better way real soon... */
3229 if (msg.wParam < 32) break;
3231 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3232 LOWORD(msg.wParam), FALSE );
3233 if (pos == (UINT)-2) fEndMenu = TRUE;
3234 else if (pos == (UINT)-1) MessageBeep(0);
3235 else
3237 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3238 TRUE, 0 );
3239 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3240 fEndMenu = (executedMenuId != -2);
3243 break;
3244 } /* switch(msg.message) - kbd */
3246 else
3248 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3249 DispatchMessageW( &msg );
3250 continue;
3253 if (!fEndMenu) fRemove = TRUE;
3255 /* finally remove message from the queue */
3257 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3258 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3259 else mt.trackFlags &= ~TF_SKIPREMOVE;
3261 __FINALLY( release_capture )
3263 /* If dropdown is still painted and the close box is clicked on
3264 then the menu will be destroyed as part of the DispatchMessage above.
3265 This will then invalidate the menu handle in mt.hTopMenu. We should
3266 check for this first. */
3267 if( IsMenu( mt.hTopMenu ) )
3269 menu = MENU_GetMenu( mt.hTopMenu );
3271 if( IsWindow( mt.hOwnerWnd ) )
3273 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3275 if (menu && (menu->wFlags & MF_POPUP))
3277 DestroyWindow( menu->hWnd );
3278 menu->hWnd = 0;
3280 if (!(wFlags & TPM_NONOTIFY))
3281 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3282 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3284 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3285 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3288 /* Reset the variable for hiding menu */
3289 if( menu ) menu->bTimeToHide = FALSE;
3292 /* The return value is only used by TrackPopupMenu */
3293 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3294 if (executedMenuId == -1) executedMenuId = 0;
3295 return executedMenuId;
3298 /***********************************************************************
3299 * MENU_InitTracking
3301 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3303 POPUPMENU *menu;
3305 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3307 HideCaret(0);
3309 /* This makes the menus of applications built with Delphi work.
3310 * It also enables menus to be displayed in more than one window,
3311 * but there are some bugs left that need to be fixed in this case.
3313 if (!bPopup && (menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3314 if (!top_popup) top_popup_hmenu = hMenu;
3316 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3317 if (!(wFlags & TPM_NONOTIFY))
3318 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3320 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3322 if (!(wFlags & TPM_NONOTIFY))
3324 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3325 /* If an app changed/recreated menu bar entries in WM_INITMENU
3326 * menu sizes will be recalculated once the menu created/shown.
3330 return TRUE;
3333 /***********************************************************************
3334 * MENU_ExitTracking
3336 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3338 TRACE("hwnd=%p\n", hWnd);
3340 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3341 ShowCaret(0);
3342 top_popup = 0;
3343 top_popup_hmenu = NULL;
3344 return TRUE;
3347 /***********************************************************************
3348 * MENU_TrackMouseMenuBar
3350 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3352 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3354 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3355 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3357 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3359 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3360 if (IsMenu(hMenu))
3362 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3364 /* fetch the window menu again, it may have changed */
3365 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3366 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3367 MENU_ExitTracking(hWnd, FALSE);
3372 /***********************************************************************
3373 * MENU_TrackKbdMenuBar
3375 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3377 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3379 UINT uItem = NO_SELECTED_ITEM;
3380 HMENU hTrackMenu;
3381 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3383 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3385 /* find window that has a menu */
3387 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3388 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3390 /* check if we have to track a system menu */
3392 hTrackMenu = GetMenu( hwnd );
3393 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3395 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3396 hTrackMenu = get_win_sys_menu( hwnd );
3397 uItem = 0;
3398 wParam |= HTSYSMENU; /* prevent item lookup */
3400 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3402 if (!IsMenu( hTrackMenu )) return;
3404 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3406 /* fetch the window menu again, it may have changed */
3407 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3409 if( wChar && wChar != ' ' )
3411 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3412 if ( uItem >= (UINT)(-2) )
3414 if( uItem == (UINT)(-1) ) MessageBeep(0);
3415 /* schedule end of menu tracking */
3416 wFlags |= TF_ENDMENU;
3417 goto track_menu;
3421 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3423 if (!(wParam & HTSYSMENU) || wChar == ' ')
3425 if( uItem == NO_SELECTED_ITEM )
3426 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3427 else
3428 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3431 track_menu:
3432 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3433 MENU_ExitTracking( hwnd, FALSE );
3436 /**********************************************************************
3437 * TrackPopupMenuEx (USER32.@)
3439 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3440 HWND hWnd, LPTPMPARAMS lpTpm )
3442 POPUPMENU *menu;
3443 BOOL ret = FALSE;
3445 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3446 hMenu, wFlags, x, y, hWnd, lpTpm,
3447 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3449 /* Parameter check */
3450 /* FIXME: this check is performed several times, here and in the called
3451 functions. That could be optimized */
3452 if (!(menu = MENU_GetMenu( hMenu )))
3454 SetLastError( ERROR_INVALID_MENU_HANDLE );
3455 return FALSE;
3458 if (IsWindow(menu->hWnd))
3460 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3461 return FALSE;
3464 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3466 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3468 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3469 if (!(wFlags & TPM_NONOTIFY))
3470 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3472 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3473 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3474 lpTpm ? &lpTpm->rcExclude : NULL );
3475 MENU_ExitTracking(hWnd, TRUE);
3477 if (menu->hWnd)
3479 DestroyWindow( menu->hWnd );
3480 menu->hWnd = 0;
3482 if (!(wFlags & TPM_NONOTIFY))
3483 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3484 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3488 return ret;
3491 /**********************************************************************
3492 * TrackPopupMenu (USER32.@)
3494 * Like the win32 API, the function return the command ID only if the
3495 * flag TPM_RETURNCMD is on.
3498 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3499 INT nReserved, HWND hWnd, const RECT *lpRect )
3501 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3504 /***********************************************************************
3505 * PopupMenuWndProc
3507 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3509 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3511 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3513 switch(message)
3515 case WM_CREATE:
3517 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3518 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3519 return 0;
3522 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3523 return MA_NOACTIVATE;
3525 case WM_PAINT:
3527 PAINTSTRUCT ps;
3528 BeginPaint( hwnd, &ps );
3529 MENU_DrawPopupMenu( hwnd, ps.hdc,
3530 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3531 EndPaint( hwnd, &ps );
3532 return 0;
3535 case WM_PRINTCLIENT:
3537 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3538 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3539 return 0;
3542 case WM_ERASEBKGND:
3543 return 1;
3545 case WM_DESTROY:
3546 /* zero out global pointer in case resident popup window was destroyed. */
3547 if (hwnd == top_popup) {
3548 top_popup = 0;
3549 top_popup_hmenu = NULL;
3551 break;
3553 case WM_SHOWWINDOW:
3555 if( wParam )
3557 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3559 else
3560 SetWindowLongPtrW( hwnd, 0, 0 );
3561 break;
3563 case MM_SETMENUHANDLE:
3564 SetWindowLongPtrW( hwnd, 0, wParam );
3565 break;
3567 case MM_GETMENUHANDLE:
3568 case MN_GETHMENU:
3569 return GetWindowLongPtrW( hwnd, 0 );
3571 default:
3572 return DefWindowProcW( hwnd, message, wParam, lParam );
3574 return 0;
3578 /***********************************************************************
3579 * MENU_GetMenuBarHeight
3581 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3583 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3584 INT orgX, INT orgY )
3586 HDC hdc;
3587 RECT rectBar;
3588 LPPOPUPMENU lppop;
3590 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3592 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3594 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3595 SelectObject( hdc, get_menu_font(FALSE));
3596 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3597 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3598 ReleaseDC( hwnd, hdc );
3599 return lppop->Height;
3603 /*******************************************************************
3604 * ChangeMenuA (USER32.@)
3606 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3607 UINT id, UINT flags )
3609 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3610 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3611 id, data );
3612 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3613 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3614 id, data );
3615 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3616 flags & MF_BYPOSITION ? pos : id,
3617 flags & ~MF_REMOVE );
3618 /* Default: MF_INSERT */
3619 return InsertMenuA( hMenu, pos, flags, id, data );
3623 /*******************************************************************
3624 * ChangeMenuW (USER32.@)
3626 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3627 UINT id, UINT flags )
3629 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3630 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3631 id, data );
3632 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3633 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3634 id, data );
3635 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3636 flags & MF_BYPOSITION ? pos : id,
3637 flags & ~MF_REMOVE );
3638 /* Default: MF_INSERT */
3639 return InsertMenuW( hMenu, pos, flags, id, data );
3643 /*******************************************************************
3644 * CheckMenuItem (USER32.@)
3646 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3648 MENUITEM *item;
3649 DWORD ret;
3651 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3652 ret = item->fState & MF_CHECKED;
3653 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3654 else item->fState &= ~MF_CHECKED;
3655 return ret;
3659 /**********************************************************************
3660 * EnableMenuItem (USER32.@)
3662 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3664 UINT oldflags;
3665 MENUITEM *item;
3666 POPUPMENU *menu;
3668 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3670 /* Get the Popupmenu to access the owner menu */
3671 if (!(menu = MENU_GetMenu(hMenu)))
3672 return (UINT)-1;
3674 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3675 return (UINT)-1;
3677 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3678 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3680 /* If the close item in the system menu change update the close button */
3681 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3683 if (menu->hSysMenuOwner != 0)
3685 RECT rc;
3686 POPUPMENU* parentMenu;
3688 /* Get the parent menu to access*/
3689 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3690 return (UINT)-1;
3692 /* Refresh the frame to reflect the change */
3693 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3694 rc.bottom = 0;
3695 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3699 return oldflags;
3703 /*******************************************************************
3704 * GetMenuStringA (USER32.@)
3706 INT WINAPI GetMenuStringA(
3707 HMENU hMenu, /* [in] menuhandle */
3708 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3709 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3710 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3711 UINT wFlags /* [in] MF_ flags */
3713 MENUITEM *item;
3715 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3716 if (str && nMaxSiz) str[0] = '\0';
3717 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3718 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3719 return 0;
3721 if (!item->text) return 0;
3722 if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3723 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3724 str[nMaxSiz-1] = 0;
3725 TRACE("returning %s\n", debugstr_a(str));
3726 return strlen(str);
3730 /*******************************************************************
3731 * GetMenuStringW (USER32.@)
3733 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3734 LPWSTR str, INT nMaxSiz, UINT wFlags )
3736 MENUITEM *item;
3738 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3739 if (str && nMaxSiz) str[0] = '\0';
3740 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3741 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3742 return 0;
3744 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3745 if( !(item->text)) {
3746 str[0] = 0;
3747 return 0;
3749 lstrcpynW( str, item->text, nMaxSiz );
3750 TRACE("returning %s\n", debugstr_w(str));
3751 return strlenW(str);
3755 /**********************************************************************
3756 * HiliteMenuItem (USER32.@)
3758 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3759 UINT wHilite )
3761 LPPOPUPMENU menu;
3762 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3763 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3764 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3765 if (menu->FocusedItem == wItemID) return TRUE;
3766 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3767 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3768 return TRUE;
3772 /**********************************************************************
3773 * GetMenuState (USER32.@)
3775 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3777 MENUITEM *item;
3778 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3779 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3780 debug_print_menuitem (" item: ", item, "");
3781 if (item->fType & MF_POPUP)
3783 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3784 if (!menu) return -1;
3785 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3787 else
3789 /* We used to (from way back then) mask the result to 0xff. */
3790 /* I don't know why and it seems wrong as the documented */
3791 /* return flag MF_SEPARATOR is outside that mask. */
3792 return (item->fType | item->fState);
3797 /**********************************************************************
3798 * GetMenuItemCount (USER32.@)
3800 INT WINAPI GetMenuItemCount( HMENU hMenu )
3802 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3803 if (!menu) return -1;
3804 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3805 return menu->nItems;
3809 /**********************************************************************
3810 * GetMenuItemID (USER32.@)
3812 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3814 MENUITEM * lpmi;
3816 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3817 if (lpmi->fType & MF_POPUP) return -1;
3818 return lpmi->wID;
3823 /**********************************************************************
3824 * MENU_mnu2mnuii
3826 * Uses flags, id and text ptr, passed by InsertMenu() and
3827 * ModifyMenu() to setup a MenuItemInfo structure.
3829 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3830 LPMENUITEMINFOW pmii)
3832 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3833 pmii->cbSize = sizeof( MENUITEMINFOW);
3834 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3835 /* setting bitmap clears text and vice versa */
3836 if( IS_STRING_ITEM(flags)) {
3837 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3838 if( !str)
3839 flags |= MF_SEPARATOR;
3840 /* Item beginning with a backspace is a help item */
3841 /* FIXME: wrong place, this is only true in win16 */
3842 else if( *str == '\b') {
3843 flags |= MF_HELP;
3844 str++;
3846 pmii->dwTypeData = (LPWSTR)str;
3847 } else if( flags & MFT_BITMAP){
3848 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3849 pmii->hbmpItem = (HBITMAP)str;
3851 if( flags & MF_OWNERDRAW){
3852 pmii->fMask |= MIIM_DATA;
3853 pmii->dwItemData = (ULONG_PTR) str;
3855 if( flags & MF_POPUP) {
3856 pmii->fMask |= MIIM_SUBMENU;
3857 pmii->hSubMenu = (HMENU)id;
3859 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3860 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3861 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3862 pmii->wID = (UINT)id;
3866 /*******************************************************************
3867 * InsertMenuW (USER32.@)
3869 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3870 UINT_PTR id, LPCWSTR str )
3872 MENUITEM *item;
3873 MENUITEMINFOW mii;
3875 if (IS_STRING_ITEM(flags) && str)
3876 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3877 hMenu, pos, flags, id, debugstr_w(str) );
3878 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3879 hMenu, pos, flags, id, str );
3881 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3882 MENU_mnu2mnuii( flags, id, str, &mii);
3883 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3885 RemoveMenu( hMenu, pos, flags );
3886 return FALSE;
3889 item->hCheckBit = item->hUnCheckBit = 0;
3890 return TRUE;
3894 /*******************************************************************
3895 * InsertMenuA (USER32.@)
3897 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3898 UINT_PTR id, LPCSTR str )
3900 BOOL ret = FALSE;
3902 if (IS_STRING_ITEM(flags) && str)
3904 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3905 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3906 if (newstr)
3908 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3909 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3910 HeapFree( GetProcessHeap(), 0, newstr );
3912 return ret;
3914 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3918 /*******************************************************************
3919 * AppendMenuA (USER32.@)
3921 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3922 UINT_PTR id, LPCSTR data )
3924 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3928 /*******************************************************************
3929 * AppendMenuW (USER32.@)
3931 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3932 UINT_PTR id, LPCWSTR data )
3934 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3938 /**********************************************************************
3939 * RemoveMenu (USER32.@)
3941 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3943 LPPOPUPMENU menu;
3944 MENUITEM *item;
3946 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3947 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3948 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3950 /* Remove item */
3952 MENU_FreeItemData( item );
3954 if (--menu->nItems == 0)
3956 HeapFree( GetProcessHeap(), 0, menu->items );
3957 menu->items = NULL;
3959 else
3961 while(nPos < menu->nItems)
3963 *item = *(item+1);
3964 item++;
3965 nPos++;
3967 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3968 menu->nItems * sizeof(MENUITEM) );
3970 return TRUE;
3974 /**********************************************************************
3975 * DeleteMenu (USER32.@)
3977 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3979 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3980 if (!item) return FALSE;
3981 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3982 /* nPos is now the position of the item */
3983 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3984 return TRUE;
3988 /*******************************************************************
3989 * ModifyMenuW (USER32.@)
3991 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3992 UINT_PTR id, LPCWSTR str )
3994 MENUITEM *item;
3995 MENUITEMINFOW mii;
3997 if (IS_STRING_ITEM(flags))
3998 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3999 else
4000 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
4002 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
4003 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4004 MENU_mnu2mnuii( flags, id, str, &mii);
4005 return SetMenuItemInfo_common( item, &mii, TRUE);
4009 /*******************************************************************
4010 * ModifyMenuA (USER32.@)
4012 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4013 UINT_PTR id, LPCSTR str )
4015 BOOL ret = FALSE;
4017 if (IS_STRING_ITEM(flags) && str)
4019 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4020 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4021 if (newstr)
4023 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4024 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4025 HeapFree( GetProcessHeap(), 0, newstr );
4027 return ret;
4029 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4033 /**********************************************************************
4034 * CreatePopupMenu (USER32.@)
4036 HMENU WINAPI CreatePopupMenu(void)
4038 HMENU hmenu;
4039 POPUPMENU *menu;
4041 if (!(hmenu = CreateMenu())) return 0;
4042 menu = MENU_GetMenu( hmenu );
4043 menu->wFlags |= MF_POPUP;
4044 menu->bTimeToHide = FALSE;
4045 return hmenu;
4049 /**********************************************************************
4050 * GetMenuCheckMarkDimensions (USER.417)
4051 * GetMenuCheckMarkDimensions (USER32.@)
4053 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4055 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4059 /**********************************************************************
4060 * SetMenuItemBitmaps (USER32.@)
4062 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4063 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4065 MENUITEM *item;
4067 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4069 if (!hNewCheck && !hNewUnCheck)
4071 item->fState &= ~MF_USECHECKBITMAPS;
4073 else /* Install new bitmaps */
4075 item->hCheckBit = hNewCheck;
4076 item->hUnCheckBit = hNewUnCheck;
4077 item->fState |= MF_USECHECKBITMAPS;
4079 return TRUE;
4083 /**********************************************************************
4084 * CreateMenu (USER32.@)
4086 HMENU WINAPI CreateMenu(void)
4088 HMENU hMenu;
4089 LPPOPUPMENU menu;
4091 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4092 menu->FocusedItem = NO_SELECTED_ITEM;
4093 menu->bTimeToHide = FALSE;
4095 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4097 TRACE("return %p\n", hMenu );
4099 return hMenu;
4103 /**********************************************************************
4104 * DestroyMenu (USER32.@)
4106 BOOL WINAPI DestroyMenu( HMENU hMenu )
4108 LPPOPUPMENU lppop;
4110 TRACE("(%p)\n", hMenu);
4112 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4113 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4115 /* DestroyMenu should not destroy system menu popup owner */
4116 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4118 DestroyWindow( lppop->hWnd );
4119 lppop->hWnd = 0;
4122 if (lppop->items) /* recursively destroy submenus */
4124 int i;
4125 MENUITEM *item = lppop->items;
4126 for (i = lppop->nItems; i > 0; i--, item++)
4128 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4129 MENU_FreeItemData( item );
4131 HeapFree( GetProcessHeap(), 0, lppop->items );
4133 HeapFree( GetProcessHeap(), 0, lppop );
4134 return TRUE;
4138 /**********************************************************************
4139 * GetSystemMenu (USER32.@)
4141 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4143 WND *wndPtr = WIN_GetPtr( hWnd );
4144 HMENU retvalue = 0;
4146 if (wndPtr == WND_DESKTOP) return 0;
4147 if (wndPtr == WND_OTHER_PROCESS)
4149 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4151 else if (wndPtr)
4153 if (wndPtr->hSysMenu && bRevert)
4155 DestroyMenu(wndPtr->hSysMenu);
4156 wndPtr->hSysMenu = 0;
4159 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4160 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4162 if( wndPtr->hSysMenu )
4164 POPUPMENU *menu;
4165 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4167 /* Store the dummy sysmenu handle to facilitate the refresh */
4168 /* of the close button if the SC_CLOSE item change */
4169 menu = MENU_GetMenu(retvalue);
4170 if ( menu )
4171 menu->hSysMenuOwner = wndPtr->hSysMenu;
4173 WIN_ReleasePtr( wndPtr );
4175 return bRevert ? 0 : retvalue;
4179 /*******************************************************************
4180 * SetSystemMenu (USER32.@)
4182 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4184 WND *wndPtr = WIN_GetPtr( hwnd );
4186 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4188 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4189 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4190 WIN_ReleasePtr( wndPtr );
4191 return TRUE;
4193 return FALSE;
4197 /**********************************************************************
4198 * GetMenu (USER32.@)
4200 HMENU WINAPI GetMenu( HWND hWnd )
4202 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4203 TRACE("for %p returning %p\n", hWnd, retvalue);
4204 return retvalue;
4207 /**********************************************************************
4208 * GetMenuBarInfo (USER32.@)
4210 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4212 POPUPMENU *menu;
4213 HMENU hmenu = NULL;
4214 ATOM class_atom;
4216 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4218 switch (idObject)
4220 case OBJID_CLIENT:
4221 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4222 if (!class_atom)
4223 return FALSE;
4224 if (class_atom != POPUPMENU_CLASS_ATOM)
4226 WARN("called on invalid window: %d\n", class_atom);
4227 SetLastError(ERROR_INVALID_MENU_HANDLE);
4228 return FALSE;
4231 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4232 break;
4233 case OBJID_MENU:
4234 hmenu = GetMenu(hwnd);
4235 break;
4236 case OBJID_SYSMENU:
4237 hmenu = GetSystemMenu(hwnd, FALSE);
4238 break;
4239 default:
4240 return FALSE;
4243 if (!hmenu)
4244 return FALSE;
4246 if (pmbi->cbSize != sizeof(MENUBARINFO))
4248 SetLastError(ERROR_INVALID_PARAMETER);
4249 return FALSE;
4252 menu = MENU_GetMenu(hmenu);
4253 if (!menu)
4254 return FALSE;
4255 if (idItem < 0 || idItem > menu->nItems)
4256 return FALSE;
4258 if (!menu->Height)
4260 SetRectEmpty(&pmbi->rcBar);
4262 else if (idItem == 0)
4264 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4265 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4266 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4268 else
4270 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4273 pmbi->hMenu = hmenu;
4274 pmbi->hwndMenu = NULL;
4275 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4276 if (idItem)
4278 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4279 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4281 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4282 if (menu)
4283 pmbi->hwndMenu = menu->hWnd;
4286 else
4288 pmbi->fFocused = pmbi->fBarFocused;
4291 return TRUE;
4294 /**********************************************************************
4295 * MENU_SetMenu
4297 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4298 * SetWindowPos call that would result if SetMenu were called directly.
4300 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4302 TRACE("(%p, %p);\n", hWnd, hMenu);
4304 if (hMenu && !IsMenu(hMenu))
4306 WARN("hMenu %p is not a menu handle\n", hMenu);
4307 return FALSE;
4309 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4310 return FALSE;
4312 hWnd = WIN_GetFullHandle( hWnd );
4313 if (GetCapture() == hWnd)
4314 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4316 if (hMenu != 0)
4318 LPPOPUPMENU lpmenu;
4320 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4322 lpmenu->hWnd = hWnd;
4323 lpmenu->Height = 0; /* Make sure we recalculate the size */
4325 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4326 return TRUE;
4330 /**********************************************************************
4331 * SetMenu (USER32.@)
4333 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4335 if(!MENU_SetMenu(hWnd, hMenu))
4336 return FALSE;
4338 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4339 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4340 return TRUE;
4344 /**********************************************************************
4345 * GetSubMenu (USER32.@)
4347 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4349 MENUITEM * lpmi;
4351 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4352 if (!(lpmi->fType & MF_POPUP)) return 0;
4353 return lpmi->hSubMenu;
4357 /**********************************************************************
4358 * DrawMenuBar (USER32.@)
4360 BOOL WINAPI DrawMenuBar( HWND hWnd )
4362 LPPOPUPMENU lppop;
4363 HMENU hMenu = GetMenu(hWnd);
4365 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4366 return FALSE;
4367 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4369 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4370 lppop->hwndOwner = hWnd;
4371 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4372 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4373 return TRUE;
4376 /***********************************************************************
4377 * DrawMenuBarTemp (USER32.@)
4379 * UNDOCUMENTED !!
4381 * called by W98SE desk.cpl Control Panel Applet
4383 * Not 100% sure about the param names, but close.
4385 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4387 LPPOPUPMENU lppop;
4388 UINT i,retvalue;
4389 HFONT hfontOld = 0;
4390 BOOL flat_menu = FALSE;
4392 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4394 if (!hMenu)
4395 hMenu = GetMenu(hwnd);
4397 if (!hFont)
4398 hFont = get_menu_font(FALSE);
4400 lppop = MENU_GetMenu( hMenu );
4401 if (lppop == NULL || lprect == NULL)
4403 retvalue = GetSystemMetrics(SM_CYMENU);
4404 goto END;
4407 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4409 hfontOld = SelectObject( hDC, hFont);
4411 if (lppop->Height == 0)
4412 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4414 lprect->bottom = lprect->top + lppop->Height;
4416 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4418 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4419 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4420 LineTo( hDC, lprect->right, lprect->bottom );
4422 if (lppop->nItems == 0)
4424 retvalue = GetSystemMetrics(SM_CYMENU);
4425 goto END;
4428 for (i = 0; i < lppop->nItems; i++)
4430 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4431 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4433 retvalue = lppop->Height;
4435 END:
4436 if (hfontOld) SelectObject (hDC, hfontOld);
4437 return retvalue;
4440 /***********************************************************************
4441 * EndMenu (USER.187)
4442 * EndMenu (USER32.@)
4444 BOOL WINAPI EndMenu(void)
4446 /* if we are in the menu code, and it is active */
4447 if (!fEndMenu && top_popup)
4449 /* terminate the menu handling code */
4450 fEndMenu = TRUE;
4452 /* needs to be posted to wakeup the internal menu handler */
4453 /* which will now terminate the menu, in the event that */
4454 /* the main window was minimized, or lost focus, so we */
4455 /* don't end up with an orphaned menu */
4456 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4458 return fEndMenu;
4462 /*****************************************************************
4463 * LoadMenuA (USER32.@)
4465 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4467 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4468 if (!hrsrc) return 0;
4469 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4473 /*****************************************************************
4474 * LoadMenuW (USER32.@)
4476 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4478 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4479 if (!hrsrc) return 0;
4480 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4484 /**********************************************************************
4485 * LoadMenuIndirectW (USER32.@)
4487 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4489 HMENU hMenu;
4490 WORD version, offset;
4491 LPCSTR p = template;
4493 version = GET_WORD(p);
4494 p += sizeof(WORD);
4495 TRACE("%p, ver %d\n", template, version );
4496 switch (version)
4498 case 0: /* standard format is version of 0 */
4499 offset = GET_WORD(p);
4500 p += sizeof(WORD) + offset;
4501 if (!(hMenu = CreateMenu())) return 0;
4502 if (!MENU_ParseResource( p, hMenu ))
4504 DestroyMenu( hMenu );
4505 return 0;
4507 return hMenu;
4508 case 1: /* extended format is version of 1 */
4509 offset = GET_WORD(p);
4510 p += sizeof(WORD) + offset;
4511 if (!(hMenu = CreateMenu())) return 0;
4512 if (!MENUEX_ParseResource( p, hMenu))
4514 DestroyMenu( hMenu );
4515 return 0;
4517 return hMenu;
4518 default:
4519 ERR("version %d not supported.\n", version);
4520 return 0;
4525 /**********************************************************************
4526 * LoadMenuIndirectA (USER32.@)
4528 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4530 return LoadMenuIndirectW( template );
4534 /**********************************************************************
4535 * IsMenu (USER32.@)
4537 BOOL WINAPI IsMenu(HMENU hmenu)
4539 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4541 if (!menu)
4543 SetLastError(ERROR_INVALID_MENU_HANDLE);
4544 return FALSE;
4546 return TRUE;
4549 /**********************************************************************
4550 * GetMenuItemInfo_common
4553 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4554 LPMENUITEMINFOW lpmii, BOOL unicode)
4556 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4558 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4560 if (!menu) {
4561 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4562 return FALSE;
4565 if( lpmii->fMask & MIIM_TYPE) {
4566 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4567 WARN("invalid combination of fMask bits used\n");
4568 /* this does not happen on Win9x/ME */
4569 SetLastError( ERROR_INVALID_PARAMETER);
4570 return FALSE;
4572 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4573 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4574 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4575 if( lpmii->fType & MFT_BITMAP) {
4576 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4577 lpmii->cch = 0;
4578 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4579 /* this does not happen on Win9x/ME */
4580 lpmii->dwTypeData = 0;
4581 lpmii->cch = 0;
4585 /* copy the text string */
4586 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4587 if( !menu->text ) {
4588 if(lpmii->dwTypeData && lpmii->cch) {
4589 lpmii->cch = 0;
4590 if( unicode)
4591 *((WCHAR *)lpmii->dwTypeData) = 0;
4592 else
4593 *((CHAR *)lpmii->dwTypeData) = 0;
4595 } else {
4596 int len;
4597 if (unicode)
4599 len = strlenW(menu->text);
4600 if(lpmii->dwTypeData && lpmii->cch)
4601 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4603 else
4605 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4606 0, NULL, NULL ) - 1;
4607 if(lpmii->dwTypeData && lpmii->cch)
4608 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4609 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4610 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4612 /* if we've copied a substring we return its length */
4613 if(lpmii->dwTypeData && lpmii->cch)
4614 if (lpmii->cch <= len + 1)
4615 lpmii->cch--;
4616 else
4617 lpmii->cch = len;
4618 else {
4619 /* return length of string */
4620 /* not on Win9x/ME if fType & MFT_BITMAP */
4621 lpmii->cch = len;
4626 if (lpmii->fMask & MIIM_FTYPE)
4627 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4629 if (lpmii->fMask & MIIM_BITMAP)
4630 lpmii->hbmpItem = menu->hbmpItem;
4632 if (lpmii->fMask & MIIM_STATE)
4633 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4635 if (lpmii->fMask & MIIM_ID)
4636 lpmii->wID = menu->wID;
4638 if (lpmii->fMask & MIIM_SUBMENU)
4639 lpmii->hSubMenu = menu->hSubMenu;
4640 else {
4641 /* hSubMenu is always cleared
4642 * (not on Win9x/ME ) */
4643 lpmii->hSubMenu = 0;
4646 if (lpmii->fMask & MIIM_CHECKMARKS) {
4647 lpmii->hbmpChecked = menu->hCheckBit;
4648 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4650 if (lpmii->fMask & MIIM_DATA)
4651 lpmii->dwItemData = menu->dwItemData;
4653 return TRUE;
4656 /**********************************************************************
4657 * GetMenuItemInfoA (USER32.@)
4659 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4660 LPMENUITEMINFOA lpmii)
4662 BOOL ret;
4663 MENUITEMINFOA mii;
4664 if( lpmii->cbSize != sizeof( mii) &&
4665 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4666 SetLastError( ERROR_INVALID_PARAMETER);
4667 return FALSE;
4669 memcpy( &mii, lpmii, lpmii->cbSize);
4670 mii.cbSize = sizeof( mii);
4671 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4672 (LPMENUITEMINFOW)&mii, FALSE);
4673 mii.cbSize = lpmii->cbSize;
4674 memcpy( lpmii, &mii, mii.cbSize);
4675 return ret;
4678 /**********************************************************************
4679 * GetMenuItemInfoW (USER32.@)
4681 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4682 LPMENUITEMINFOW lpmii)
4684 BOOL ret;
4685 MENUITEMINFOW mii;
4686 if( lpmii->cbSize != sizeof( mii) &&
4687 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4688 SetLastError( ERROR_INVALID_PARAMETER);
4689 return FALSE;
4691 memcpy( &mii, lpmii, lpmii->cbSize);
4692 mii.cbSize = sizeof( mii);
4693 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4694 mii.cbSize = lpmii->cbSize;
4695 memcpy( lpmii, &mii, mii.cbSize);
4696 return ret;
4700 /* set a menu item text from a ASCII or Unicode string */
4701 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4703 if (!text)
4704 menu->text = NULL;
4705 else if (unicode)
4707 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4708 strcpyW( menu->text, text );
4710 else
4712 LPCSTR str = (LPCSTR)text;
4713 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4714 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4715 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4720 /**********************************************************************
4721 * MENU_depth
4723 * detect if there are loops in the menu tree (or the depth is too large)
4725 static int MENU_depth( POPUPMENU *pmenu, int depth)
4727 UINT i;
4728 MENUITEM *item;
4729 int subdepth;
4731 depth++;
4732 if( depth > MAXMENUDEPTH) return depth;
4733 item = pmenu->items;
4734 subdepth = depth;
4735 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4736 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4737 if( psubmenu){
4738 int bdepth = MENU_depth( psubmenu, depth);
4739 if( bdepth > subdepth) subdepth = bdepth;
4741 if( subdepth > MAXMENUDEPTH)
4742 TRACE("<- hmenu %p\n", item->hSubMenu);
4744 return subdepth;
4748 /**********************************************************************
4749 * SetMenuItemInfo_common
4751 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4752 * MIIM_BITMAP and MIIM_STRING flags instead.
4755 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4756 const MENUITEMINFOW *lpmii,
4757 BOOL unicode)
4759 if (!menu) return FALSE;
4761 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4763 if (lpmii->fMask & MIIM_FTYPE ) {
4764 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4765 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4767 if (lpmii->fMask & MIIM_STRING ) {
4768 /* free the string when used */
4769 HeapFree(GetProcessHeap(), 0, menu->text);
4770 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4773 if (lpmii->fMask & MIIM_STATE)
4774 /* Other menu items having MFS_DEFAULT are not converted
4775 to normal items */
4776 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4778 if (lpmii->fMask & MIIM_ID)
4779 menu->wID = lpmii->wID;
4781 if (lpmii->fMask & MIIM_SUBMENU) {
4782 menu->hSubMenu = lpmii->hSubMenu;
4783 if (menu->hSubMenu) {
4784 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4785 if (subMenu) {
4786 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4787 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4788 menu->hSubMenu = 0;
4789 return FALSE;
4791 subMenu->wFlags |= MF_POPUP;
4792 menu->fType |= MF_POPUP;
4793 } else {
4794 SetLastError( ERROR_INVALID_PARAMETER);
4795 return FALSE;
4798 else
4799 menu->fType &= ~MF_POPUP;
4802 if (lpmii->fMask & MIIM_CHECKMARKS)
4804 menu->hCheckBit = lpmii->hbmpChecked;
4805 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4807 if (lpmii->fMask & MIIM_DATA)
4808 menu->dwItemData = lpmii->dwItemData;
4810 if (lpmii->fMask & MIIM_BITMAP)
4811 menu->hbmpItem = lpmii->hbmpItem;
4813 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4814 menu->fType |= MFT_SEPARATOR;
4816 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4817 return TRUE;
4820 /**********************************************************************
4821 * MENU_NormalizeMenuItemInfoStruct
4823 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4824 * check, copy and extend the MENUITEMINFO struct from the version that the application
4825 * supplied to the version used by wine source. */
4826 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4827 MENUITEMINFOW *pmii_out )
4829 /* do we recognize the size? */
4830 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4831 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4832 SetLastError( ERROR_INVALID_PARAMETER);
4833 return FALSE;
4835 /* copy the fields that we have */
4836 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4837 /* if the hbmpItem member is missing then extend */
4838 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4839 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4840 pmii_out->hbmpItem = NULL;
4842 /* test for invalid bit combinations */
4843 if( (pmii_out->fMask & MIIM_TYPE &&
4844 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4845 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4846 WARN("invalid combination of fMask bits used\n");
4847 /* this does not happen on Win9x/ME */
4848 SetLastError( ERROR_INVALID_PARAMETER);
4849 return FALSE;
4851 /* convert old style (MIIM_TYPE) to the new */
4852 if( pmii_out->fMask & MIIM_TYPE){
4853 pmii_out->fMask |= MIIM_FTYPE;
4854 if( IS_STRING_ITEM(pmii_out->fType)){
4855 pmii_out->fMask |= MIIM_STRING;
4856 } else if( (pmii_out->fType) & MFT_BITMAP){
4857 pmii_out->fMask |= MIIM_BITMAP;
4858 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4861 return TRUE;
4864 /**********************************************************************
4865 * SetMenuItemInfoA (USER32.@)
4867 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4868 const MENUITEMINFOA *lpmii)
4870 MENUITEMINFOW mii;
4872 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4874 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4876 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4877 &mii, FALSE);
4880 /**********************************************************************
4881 * SetMenuItemInfoW (USER32.@)
4883 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4884 const MENUITEMINFOW *lpmii)
4886 MENUITEMINFOW mii;
4888 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4890 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4891 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4892 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4895 /**********************************************************************
4896 * SetMenuDefaultItem (USER32.@)
4899 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4901 UINT i;
4902 POPUPMENU *menu;
4903 MENUITEM *item;
4905 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4907 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4909 /* reset all default-item flags */
4910 item = menu->items;
4911 for (i = 0; i < menu->nItems; i++, item++)
4913 item->fState &= ~MFS_DEFAULT;
4916 /* no default item */
4917 if ( -1 == uItem)
4919 return TRUE;
4922 item = menu->items;
4923 if ( bypos )
4925 if ( uItem >= menu->nItems ) return FALSE;
4926 item[uItem].fState |= MFS_DEFAULT;
4927 return TRUE;
4929 else
4931 for (i = 0; i < menu->nItems; i++, item++)
4933 if (item->wID == uItem)
4935 item->fState |= MFS_DEFAULT;
4936 return TRUE;
4941 return FALSE;
4944 /**********************************************************************
4945 * GetMenuDefaultItem (USER32.@)
4947 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4949 POPUPMENU *menu;
4950 MENUITEM * item;
4951 UINT i = 0;
4953 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4955 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4957 /* find default item */
4958 item = menu->items;
4960 /* empty menu */
4961 if (! item) return -1;
4963 while ( !( item->fState & MFS_DEFAULT ) )
4965 i++; item++;
4966 if (i >= menu->nItems ) return -1;
4969 /* default: don't return disabled items */
4970 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4972 /* search rekursiv when needed */
4973 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4975 UINT ret;
4976 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4977 if ( -1 != ret ) return ret;
4979 /* when item not found in submenu, return the popup item */
4981 return ( bypos ) ? i : item->wID;
4986 /**********************************************************************
4987 * InsertMenuItemA (USER32.@)
4989 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4990 const MENUITEMINFOA *lpmii)
4992 MENUITEM *item;
4993 MENUITEMINFOW mii;
4995 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4997 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4999 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5000 return SetMenuItemInfo_common(item, &mii, FALSE);
5004 /**********************************************************************
5005 * InsertMenuItemW (USER32.@)
5007 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5008 const MENUITEMINFOW *lpmii)
5010 MENUITEM *item;
5011 MENUITEMINFOW mii;
5013 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5015 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5017 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5018 return SetMenuItemInfo_common(item, &mii, TRUE);
5021 /**********************************************************************
5022 * CheckMenuRadioItem (USER32.@)
5025 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5026 UINT first, UINT last, UINT check,
5027 UINT bypos)
5029 BOOL done = FALSE;
5030 UINT i;
5031 MENUITEM *mi_first = NULL, *mi_check;
5032 HMENU m_first, m_check;
5034 for (i = first; i <= last; i++)
5036 UINT pos = i;
5038 if (!mi_first)
5040 m_first = hMenu;
5041 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5042 if (!mi_first) continue;
5043 mi_check = mi_first;
5044 m_check = m_first;
5046 else
5048 m_check = hMenu;
5049 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5050 if (!mi_check) continue;
5053 if (m_first != m_check) continue;
5054 if (mi_check->fType == MFT_SEPARATOR) continue;
5056 if (i == check)
5058 mi_check->fType |= MFT_RADIOCHECK;
5059 mi_check->fState |= MFS_CHECKED;
5060 done = TRUE;
5062 else
5064 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5065 mi_check->fState &= ~MFS_CHECKED;
5069 return done;
5073 /**********************************************************************
5074 * GetMenuItemRect (USER32.@)
5076 * ATTENTION: Here, the returned values in rect are the screen
5077 * coordinates of the item just like if the menu was
5078 * always on the upper left side of the application.
5081 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5082 LPRECT rect)
5084 POPUPMENU *itemMenu;
5085 MENUITEM *item;
5086 HWND referenceHwnd;
5088 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5090 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5091 referenceHwnd = hwnd;
5093 if(!hwnd)
5095 itemMenu = MENU_GetMenu(hMenu);
5096 if (itemMenu == NULL)
5097 return FALSE;
5099 if(itemMenu->hWnd == 0)
5100 return FALSE;
5101 referenceHwnd = itemMenu->hWnd;
5104 if ((rect == NULL) || (item == NULL))
5105 return FALSE;
5107 *rect = item->rect;
5109 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5111 return TRUE;
5114 /**********************************************************************
5115 * SetMenuInfo (USER32.@)
5117 * FIXME
5118 * actually use the items to draw the menu
5119 * (recalculate and/or redraw)
5121 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5123 POPUPMENU *menu;
5124 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5126 if (lpmi->fMask & MIM_BACKGROUND)
5127 menu->hbrBack = lpmi->hbrBack;
5129 if (lpmi->fMask & MIM_HELPID)
5130 menu->dwContextHelpID = lpmi->dwContextHelpID;
5132 if (lpmi->fMask & MIM_MAXHEIGHT)
5133 menu->cyMax = lpmi->cyMax;
5135 if (lpmi->fMask & MIM_MENUDATA)
5136 menu->dwMenuData = lpmi->dwMenuData;
5138 if (lpmi->fMask & MIM_STYLE)
5139 menu->dwStyle = lpmi->dwStyle;
5141 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5142 int i;
5143 MENUITEM *item = menu->items;
5144 for( i = menu->nItems; i; i--, item++)
5145 if( item->fType & MF_POPUP)
5146 menu_SetMenuInfo( item->hSubMenu, lpmi);
5148 return TRUE;
5151 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5153 TRACE("(%p %p)\n", hMenu, lpmi);
5154 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5155 if( lpmi->fMask & MIM_STYLE) {
5156 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5157 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5158 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5160 return TRUE;
5162 SetLastError( ERROR_INVALID_PARAMETER);
5163 return FALSE;
5166 /**********************************************************************
5167 * GetMenuInfo (USER32.@)
5169 * NOTES
5170 * win98/NT5.0
5173 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5174 { POPUPMENU *menu;
5176 TRACE("(%p %p)\n", hMenu, lpmi);
5178 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5181 if (lpmi->fMask & MIM_BACKGROUND)
5182 lpmi->hbrBack = menu->hbrBack;
5184 if (lpmi->fMask & MIM_HELPID)
5185 lpmi->dwContextHelpID = menu->dwContextHelpID;
5187 if (lpmi->fMask & MIM_MAXHEIGHT)
5188 lpmi->cyMax = menu->cyMax;
5190 if (lpmi->fMask & MIM_MENUDATA)
5191 lpmi->dwMenuData = menu->dwMenuData;
5193 if (lpmi->fMask & MIM_STYLE)
5194 lpmi->dwStyle = menu->dwStyle;
5196 return TRUE;
5198 SetLastError( ERROR_INVALID_PARAMETER);
5199 return FALSE;
5203 /**********************************************************************
5204 * SetMenuContextHelpId (USER32.@)
5206 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5208 LPPOPUPMENU menu;
5210 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5212 if ((menu = MENU_GetMenu(hMenu)))
5214 menu->dwContextHelpID = dwContextHelpID;
5215 return TRUE;
5217 return FALSE;
5221 /**********************************************************************
5222 * GetMenuContextHelpId (USER32.@)
5224 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5226 LPPOPUPMENU menu;
5228 TRACE("(%p)\n", hMenu);
5230 if ((menu = MENU_GetMenu(hMenu)))
5232 return menu->dwContextHelpID;
5234 return 0;
5237 /**********************************************************************
5238 * MenuItemFromPoint (USER32.@)
5240 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5242 POPUPMENU *menu = MENU_GetMenu(hMenu);
5243 UINT pos;
5245 /*FIXME: Do we have to handle hWnd here? */
5246 if (!menu) return -1;
5247 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5248 return pos;
5252 /**********************************************************************
5253 * translate_accelerator
5255 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5256 BYTE fVirt, WORD key, WORD cmd )
5258 INT mask = 0;
5259 UINT mesg = 0;
5261 if (wParam != key) return FALSE;
5263 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5264 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5265 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5267 if (message == WM_CHAR || message == WM_SYSCHAR)
5269 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5271 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5272 goto found;
5275 else
5277 if(fVirt & FVIRTKEY)
5279 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5280 wParam, 0xff & HIWORD(lParam));
5282 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5283 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5285 else
5287 if (!(lParam & 0x01000000)) /* no special_key */
5289 if ((fVirt & FALT) && (lParam & 0x20000000))
5290 { /* ^^ ALT pressed */
5291 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5292 goto found;
5297 return FALSE;
5299 found:
5300 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5301 mesg = 1;
5302 else
5304 HMENU hMenu, hSubMenu, hSysMenu;
5305 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5307 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5308 hSysMenu = get_win_sys_menu( hWnd );
5310 /* find menu item and ask application to initialize it */
5311 /* 1. in the system menu */
5312 hSubMenu = hSysMenu;
5313 nPos = cmd;
5314 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5316 if (GetCapture())
5317 mesg = 2;
5318 if (!IsWindowEnabled(hWnd))
5319 mesg = 3;
5320 else
5322 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5323 if(hSubMenu != hSysMenu)
5325 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5326 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5327 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5329 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5332 else /* 2. in the window's menu */
5334 hSubMenu = hMenu;
5335 nPos = cmd;
5336 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5338 if (GetCapture())
5339 mesg = 2;
5340 if (!IsWindowEnabled(hWnd))
5341 mesg = 3;
5342 else
5344 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5345 if(hSubMenu != hMenu)
5347 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5348 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5349 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5351 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5356 if (mesg == 0)
5358 if (uSysStat != (UINT)-1)
5360 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5361 mesg=4;
5362 else
5363 mesg=WM_SYSCOMMAND;
5365 else
5367 if (uStat != (UINT)-1)
5369 if (IsIconic(hWnd))
5370 mesg=5;
5371 else
5373 if (uStat & (MF_DISABLED|MF_GRAYED))
5374 mesg=6;
5375 else
5376 mesg=WM_COMMAND;
5379 else
5380 mesg=WM_COMMAND;
5385 if( mesg==WM_COMMAND )
5387 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5388 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5390 else if( mesg==WM_SYSCOMMAND )
5392 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5393 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5395 else
5397 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5398 * #0: unknown (please report!)
5399 * #1: for WM_KEYUP,WM_SYSKEYUP
5400 * #2: mouse is captured
5401 * #3: window is disabled
5402 * #4: it's a disabled system menu option
5403 * #5: it's a menu option, but window is iconic
5404 * #6: it's a menu option, but disabled
5406 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5407 if(mesg==0)
5408 ERR_(accel)(" unknown reason - please report!\n");
5410 return TRUE;
5413 /**********************************************************************
5414 * TranslateAcceleratorA (USER32.@)
5415 * TranslateAccelerator (USER32.@)
5417 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5419 switch (msg->message)
5421 case WM_KEYDOWN:
5422 case WM_SYSKEYDOWN:
5423 return TranslateAcceleratorW( hWnd, hAccel, msg );
5425 case WM_CHAR:
5426 case WM_SYSCHAR:
5428 MSG msgW = *msg;
5429 char ch = LOWORD(msg->wParam);
5430 WCHAR wch;
5431 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5432 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5433 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5436 default:
5437 return 0;
5441 /**********************************************************************
5442 * TranslateAcceleratorW (USER32.@)
5444 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5446 ACCEL data[32], *ptr = data;
5447 int i, count;
5449 if (!hWnd) return 0;
5451 if (msg->message != WM_KEYDOWN &&
5452 msg->message != WM_SYSKEYDOWN &&
5453 msg->message != WM_CHAR &&
5454 msg->message != WM_SYSCHAR)
5455 return 0;
5457 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5458 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5460 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5461 if (count > sizeof(data)/sizeof(data[0]))
5463 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5465 count = CopyAcceleratorTableW( hAccel, ptr, count );
5466 for (i = 0; i < count; i++)
5468 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5469 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5470 break;
5472 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5473 return (i < count);