include: Define WINE_RB_ENTRY_VALUE using the standard offsetof.
[wine/multimedia.git] / dlls / user32 / menu.c
blobb587e634fb29ecf65b5b693cf022738529efd660
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 int 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 int 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,
1805 BOOL suppress_draw)
1807 LPPOPUPMENU lppop;
1808 HFONT hfontOld = 0;
1809 HMENU hMenu = GetMenu(hwnd);
1811 lppop = MENU_GetMenu( hMenu );
1812 if (lppop == NULL || lprect == NULL)
1814 return GetSystemMetrics(SM_CYMENU);
1817 if (suppress_draw)
1819 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1821 if (lppop->Height == 0)
1822 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1824 lprect->bottom = lprect->top + lppop->Height;
1826 if (hfontOld) SelectObject( hDC, hfontOld);
1827 return lppop->Height;
1829 else
1830 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1834 /***********************************************************************
1835 * MENU_ShowPopup
1837 * Display a popup menu.
1839 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1840 INT x, INT y, INT xanchor, INT yanchor )
1842 POPUPMENU *menu;
1843 INT width, height;
1844 POINT pt;
1845 HMONITOR monitor;
1846 MONITORINFO info;
1847 DWORD ex_style = 0;
1849 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1850 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1852 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1853 if (menu->FocusedItem != NO_SELECTED_ITEM)
1855 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1856 menu->FocusedItem = NO_SELECTED_ITEM;
1859 /* store the owner for DrawItem */
1860 if (!IsWindow( hwndOwner ))
1862 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1863 return FALSE;
1865 menu->hwndOwner = hwndOwner;
1867 menu->nScrollPos = 0;
1868 MENU_PopupMenuCalcSize( menu );
1870 /* adjust popup menu pos so that it fits within the desktop */
1872 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1873 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1875 /* FIXME: should use item rect */
1876 pt.x = x;
1877 pt.y = y;
1878 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1879 info.cbSize = sizeof(info);
1880 GetMonitorInfoW( monitor, &info );
1882 if (flags & TPM_LAYOUTRTL)
1884 ex_style = WS_EX_LAYOUTRTL;
1885 flags ^= TPM_RIGHTALIGN;
1888 if( flags & TPM_RIGHTALIGN ) x -= width;
1889 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1891 if( flags & TPM_BOTTOMALIGN ) y -= height;
1892 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1894 if( x + width > info.rcWork.right)
1896 if( xanchor && x >= width - xanchor )
1897 x -= width - xanchor;
1899 if( x + width > info.rcWork.right)
1900 x = info.rcWork.right - width;
1902 if( x < info.rcWork.left ) x = info.rcWork.left;
1904 if( y + height > info.rcWork.bottom)
1906 if( yanchor && y >= height + yanchor )
1907 y -= height + yanchor;
1909 if( y + height > info.rcWork.bottom)
1910 y = info.rcWork.bottom - height;
1912 if( y < info.rcWork.top ) y = info.rcWork.top;
1914 /* NOTE: In Windows, top menu popup is not owned. */
1915 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1916 WS_POPUP, x, y, width, height,
1917 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1918 (LPVOID)hmenu );
1919 if( !menu->hWnd ) return FALSE;
1920 if (!top_popup) {
1921 top_popup = menu->hWnd;
1922 top_popup_hmenu = hmenu;
1924 /* Display the window */
1926 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1927 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1928 UpdateWindow( menu->hWnd );
1929 return TRUE;
1933 /***********************************************************************
1934 * MENU_EnsureMenuItemVisible
1936 static void
1937 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1939 if (lppop->bScrolling)
1941 MENUITEM *item = &lppop->items[wIndex];
1942 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1943 UINT nOldPos = lppop->nScrollPos;
1944 RECT rc;
1945 UINT arrow_bitmap_height;
1946 BITMAP bmp;
1948 GetClientRect(lppop->hWnd, &rc);
1950 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1951 arrow_bitmap_height = bmp.bmHeight;
1953 rc.top += arrow_bitmap_height;
1954 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1956 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1957 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1960 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1961 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1962 MENU_DrawScrollArrows(lppop, hdc);
1964 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1966 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1967 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1968 MENU_DrawScrollArrows(lppop, hdc);
1974 /***********************************************************************
1975 * MENU_SelectItem
1977 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1978 BOOL sendMenuSelect, HMENU topmenu )
1980 LPPOPUPMENU lppop;
1981 HDC hdc;
1983 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1985 lppop = MENU_GetMenu( hmenu );
1986 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1988 if (lppop->FocusedItem == wIndex) return;
1989 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1990 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1991 if (!top_popup) {
1992 top_popup = lppop->hWnd;
1993 top_popup_hmenu = hmenu;
1996 SelectObject( hdc, get_menu_font(FALSE));
1998 /* Clear previous highlighted item */
1999 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2001 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2002 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2003 lppop->Height, !(lppop->wFlags & MF_POPUP),
2004 ODA_SELECT );
2007 /* Highlight new item (if any) */
2008 lppop->FocusedItem = wIndex;
2009 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2011 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2012 lppop->items[wIndex].fState |= MF_HILITE;
2013 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2014 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2015 &lppop->items[wIndex], lppop->Height,
2016 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2018 if (sendMenuSelect)
2020 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2021 SendMessageW( hwndOwner, WM_MENUSELECT,
2022 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2023 ip->fType | ip->fState |
2024 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2027 else if (sendMenuSelect) {
2028 if(topmenu){
2029 int pos;
2030 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2031 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2032 MENUITEM *ip = &ptm->items[pos];
2033 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2034 ip->fType | ip->fState |
2035 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2039 ReleaseDC( lppop->hWnd, hdc );
2043 /***********************************************************************
2044 * MENU_MoveSelection
2046 * Moves currently selected item according to the offset parameter.
2047 * If there is no selection then it should select the last item if
2048 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2050 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2052 INT i;
2053 POPUPMENU *menu;
2055 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2057 menu = MENU_GetMenu( hmenu );
2058 if ((!menu) || (!menu->items)) return;
2060 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2062 if( menu->nItems == 1 ) return; else
2063 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2064 ; i += offset)
2065 if (!(menu->items[i].fType & MF_SEPARATOR))
2067 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2068 return;
2072 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2073 i >= 0 && i < menu->nItems ; i += offset)
2074 if (!(menu->items[i].fType & MF_SEPARATOR))
2076 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2077 return;
2082 /**********************************************************************
2083 * MENU_InsertItem
2085 * Insert (allocate) a new item into a menu.
2087 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2089 MENUITEM *newItems;
2090 POPUPMENU *menu;
2092 if (!(menu = MENU_GetMenu(hMenu)))
2093 return NULL;
2095 /* Find where to insert new item */
2097 if (flags & MF_BYPOSITION) {
2098 if (pos > menu->nItems)
2099 pos = menu->nItems;
2100 } else {
2101 if (!MENU_FindItem( &hMenu, &pos, flags ))
2102 pos = menu->nItems;
2103 else {
2104 if (!(menu = MENU_GetMenu( hMenu )))
2105 return NULL;
2109 /* Make sure that MDI system buttons stay on the right side.
2110 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2111 * regardless of their id.
2113 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2114 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2115 pos--;
2117 TRACE("inserting at %u flags %x\n", pos, flags);
2119 /* Create new items array */
2121 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2122 if (!newItems)
2124 WARN("allocation failed\n" );
2125 return NULL;
2127 if (menu->nItems > 0)
2129 /* Copy the old array into the new one */
2130 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2131 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2132 (menu->nItems-pos)*sizeof(MENUITEM) );
2133 HeapFree( GetProcessHeap(), 0, menu->items );
2135 menu->items = newItems;
2136 menu->nItems++;
2137 memset( &newItems[pos], 0, sizeof(*newItems) );
2138 menu->Height = 0; /* force size recalculate */
2139 return &newItems[pos];
2143 /**********************************************************************
2144 * MENU_ParseResource
2146 * Parse a standard menu resource and add items to the menu.
2147 * Return a pointer to the end of the resource.
2149 * NOTE: flags is equivalent to the mtOption field
2151 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2153 WORD flags, id = 0;
2154 LPCWSTR str;
2155 BOOL end_flag;
2159 flags = GET_WORD(res);
2160 end_flag = flags & MF_END;
2161 /* Remove MF_END because it has the same value as MF_HILITE */
2162 flags &= ~MF_END;
2163 res += sizeof(WORD);
2164 if (!(flags & MF_POPUP))
2166 id = GET_WORD(res);
2167 res += sizeof(WORD);
2169 str = (LPCWSTR)res;
2170 res += (strlenW(str) + 1) * sizeof(WCHAR);
2171 if (flags & MF_POPUP)
2173 HMENU hSubMenu = CreatePopupMenu();
2174 if (!hSubMenu) return NULL;
2175 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2176 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2178 else /* Not a popup */
2180 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2182 } while (!end_flag);
2183 return res;
2187 /**********************************************************************
2188 * MENUEX_ParseResource
2190 * Parse an extended menu resource and add items to the menu.
2191 * Return a pointer to the end of the resource.
2193 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2195 WORD resinfo;
2196 do {
2197 MENUITEMINFOW mii;
2199 mii.cbSize = sizeof(mii);
2200 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2201 mii.fType = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 mii.fState = GET_DWORD(res);
2204 res += sizeof(DWORD);
2205 mii.wID = GET_DWORD(res);
2206 res += sizeof(DWORD);
2207 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2208 res += sizeof(WORD);
2209 /* Align the text on a word boundary. */
2210 res += (~((UINT_PTR)res - 1)) & 1;
2211 mii.dwTypeData = (LPWSTR) res;
2212 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2213 /* Align the following fields on a dword boundary. */
2214 res += (~((UINT_PTR)res - 1)) & 3;
2216 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2217 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2219 if (resinfo & 1) { /* Pop-up? */
2220 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2221 res += sizeof(DWORD);
2222 mii.hSubMenu = CreatePopupMenu();
2223 if (!mii.hSubMenu)
2224 return NULL;
2225 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2226 DestroyMenu(mii.hSubMenu);
2227 return NULL;
2229 mii.fMask |= MIIM_SUBMENU;
2230 mii.fType |= MF_POPUP;
2232 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2234 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2235 mii.wID, mii.fType);
2236 mii.fType |= MF_SEPARATOR;
2238 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2239 } while (!(resinfo & MF_END));
2240 return res;
2244 /***********************************************************************
2245 * MENU_GetSubPopup
2247 * Return the handle of the selected sub-popup menu (if any).
2249 static HMENU MENU_GetSubPopup( HMENU hmenu )
2251 POPUPMENU *menu;
2252 MENUITEM *item;
2254 menu = MENU_GetMenu( hmenu );
2256 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2258 item = &menu->items[menu->FocusedItem];
2259 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2260 return item->hSubMenu;
2261 return 0;
2265 /***********************************************************************
2266 * MENU_HideSubPopups
2268 * Hide the sub-popup menus of this menu.
2270 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2271 BOOL sendMenuSelect, UINT wFlags )
2273 POPUPMENU *menu = MENU_GetMenu( hmenu );
2275 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2277 if (menu && top_popup)
2279 HMENU hsubmenu;
2280 POPUPMENU *submenu;
2281 MENUITEM *item;
2283 if (menu->FocusedItem != NO_SELECTED_ITEM)
2285 item = &menu->items[menu->FocusedItem];
2286 if (!(item->fType & MF_POPUP) ||
2287 !(item->fState & MF_MOUSESELECT)) return;
2288 item->fState &= ~MF_MOUSESELECT;
2289 hsubmenu = item->hSubMenu;
2290 } else return;
2292 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2293 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2294 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2295 DestroyWindow( submenu->hWnd );
2296 submenu->hWnd = 0;
2298 if (!(wFlags & TPM_NONOTIFY))
2299 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2300 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2305 /***********************************************************************
2306 * MENU_ShowSubPopup
2308 * Display the sub-menu of the selected item of this menu.
2309 * Return the handle of the submenu, or hmenu if no submenu to display.
2311 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2312 BOOL selectFirst, UINT wFlags )
2314 RECT rect;
2315 POPUPMENU *menu;
2316 MENUITEM *item;
2317 HDC hdc;
2319 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2321 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2323 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2325 item = &menu->items[menu->FocusedItem];
2326 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2327 return hmenu;
2329 /* message must be sent before using item,
2330 because nearly everything may be changed by the application ! */
2332 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2333 if (!(wFlags & TPM_NONOTIFY))
2334 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2335 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2337 item = &menu->items[menu->FocusedItem];
2338 rect = item->rect;
2340 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2341 if (!(item->fState & MF_HILITE))
2343 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2344 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2346 SelectObject( hdc, get_menu_font(FALSE));
2348 item->fState |= MF_HILITE;
2349 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2350 ReleaseDC( menu->hWnd, hdc );
2352 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2353 item->rect = rect;
2355 item->fState |= MF_MOUSESELECT;
2357 if (IS_SYSTEM_MENU(menu))
2359 MENU_InitSysMenuPopup(item->hSubMenu,
2360 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2361 GetClassLongW( menu->hWnd, GCL_STYLE));
2363 NC_GetSysPopupPos( menu->hWnd, &rect );
2364 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2365 rect.top = rect.bottom;
2366 rect.right = GetSystemMetrics(SM_CXSIZE);
2367 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2369 else
2371 GetWindowRect( menu->hWnd, &rect );
2372 if (menu->wFlags & MF_POPUP)
2374 RECT rc = item->rect;
2376 MENU_AdjustMenuItemRect(menu, &rc);
2378 /* The first item in the popup menu has to be at the
2379 same y position as the focused menu item */
2380 if (wFlags & TPM_LAYOUTRTL)
2381 rect.left += GetSystemMetrics(SM_CXBORDER);
2382 else
2383 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2384 rect.top += rc.top - MENU_TOP_MARGIN;
2385 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2386 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2387 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2389 else
2391 if (wFlags & TPM_LAYOUTRTL)
2392 rect.left = rect.right - item->rect.left;
2393 else
2394 rect.left += item->rect.left;
2395 rect.top += item->rect.bottom;
2396 rect.right = item->rect.right - item->rect.left;
2397 rect.bottom = item->rect.bottom - item->rect.top;
2401 /* use default alignment for submenus */
2402 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2404 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2405 rect.left, rect.top, rect.right, rect.bottom );
2406 if (selectFirst)
2407 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2408 return item->hSubMenu;
2413 /**********************************************************************
2414 * MENU_IsMenuActive
2416 HWND MENU_IsMenuActive(void)
2418 return top_popup;
2421 /**********************************************************************
2422 * MENU_EndMenu
2424 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2426 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2428 void MENU_EndMenu( HWND hwnd )
2430 POPUPMENU *menu;
2431 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2432 if (menu && hwnd == menu->hwndOwner) EndMenu();
2435 /***********************************************************************
2436 * MENU_PtMenu
2438 * Walks menu chain trying to find a menu pt maps to.
2440 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2442 POPUPMENU *menu = MENU_GetMenu( hMenu );
2443 UINT item = menu->FocusedItem;
2444 HMENU ret;
2446 /* try subpopup first (if any) */
2447 ret = (item != NO_SELECTED_ITEM &&
2448 (menu->items[item].fType & MF_POPUP) &&
2449 (menu->items[item].fState & MF_MOUSESELECT))
2450 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2452 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2454 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2455 if( menu->wFlags & MF_POPUP )
2457 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2459 else if (ht == HTSYSMENU)
2460 ret = get_win_sys_menu( menu->hWnd );
2461 else if (ht == HTMENU)
2462 ret = GetMenu( menu->hWnd );
2464 return ret;
2467 /***********************************************************************
2468 * MENU_ExecFocusedItem
2470 * Execute a menu item (for instance when user pressed Enter).
2471 * Return the wID of the executed item. Otherwise, -1 indicating
2472 * that no menu item was executed, -2 if a popup is shown;
2473 * Have to receive the flags for the TrackPopupMenu options to avoid
2474 * sending unwanted message.
2477 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2479 MENUITEM *item;
2480 POPUPMENU *menu = MENU_GetMenu( hMenu );
2482 TRACE("%p hmenu=%p\n", pmt, hMenu);
2484 if (!menu || !menu->nItems ||
2485 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2487 item = &menu->items[menu->FocusedItem];
2489 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2491 if (!(item->fType & MF_POPUP))
2493 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2495 /* If TPM_RETURNCMD is set you return the id, but
2496 do not send a message to the owner */
2497 if(!(wFlags & TPM_RETURNCMD))
2499 if( menu->wFlags & MF_SYSMENU )
2500 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2501 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2502 else
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2505 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2507 if (dwStyle & MNS_NOTIFYBYPOS)
2508 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2509 (LPARAM)hMenu);
2510 else
2511 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2514 return item->wID;
2517 else
2519 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2520 return -2;
2523 return -1;
2526 /***********************************************************************
2527 * MENU_SwitchTracking
2529 * Helper function for menu navigation routines.
2531 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2536 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2538 if( pmt->hTopMenu != hPtMenu &&
2539 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2541 /* both are top level menus (system and menu-bar) */
2542 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2543 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2544 pmt->hTopMenu = hPtMenu;
2546 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2547 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2551 /***********************************************************************
2552 * MENU_ButtonDown
2554 * Return TRUE if we can go on with menu tracking.
2556 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2558 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2560 if (hPtMenu)
2562 UINT id = 0;
2563 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2564 MENUITEM *item;
2566 if( IS_SYSTEM_MENU(ptmenu) )
2567 item = ptmenu->items;
2568 else
2569 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2571 if( item )
2573 if( ptmenu->FocusedItem != id )
2574 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2576 /* If the popup menu is not already "popped" */
2577 if(!(item->fState & MF_MOUSESELECT ))
2579 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2582 return TRUE;
2584 /* Else the click was on the menu bar, finish the tracking */
2586 return FALSE;
2589 /***********************************************************************
2590 * MENU_ButtonUp
2592 * Return the value of MENU_ExecFocusedItem if
2593 * the selected item was not a popup. Else open the popup.
2594 * A -1 return value indicates that we go on with menu tracking.
2597 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2599 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2601 if (hPtMenu)
2603 UINT id = 0;
2604 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2605 MENUITEM *item;
2607 if( IS_SYSTEM_MENU(ptmenu) )
2608 item = ptmenu->items;
2609 else
2610 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2612 if( item && (ptmenu->FocusedItem == id ))
2614 debug_print_menuitem ("FocusedItem: ", item, "");
2616 if( !(item->fType & MF_POPUP) )
2618 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2619 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2620 return executedMenuId;
2623 /* If we are dealing with the top-level menu */
2624 /* and this is a click on an already "popped" item: */
2625 /* Stop the menu tracking and close the opened submenus */
2626 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2627 return 0;
2629 ptmenu->bTimeToHide = TRUE;
2631 return -1;
2635 /***********************************************************************
2636 * MENU_MouseMove
2638 * Return TRUE if we can go on with menu tracking.
2640 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2642 UINT id = NO_SELECTED_ITEM;
2643 POPUPMENU *ptmenu = NULL;
2645 if( hPtMenu )
2647 ptmenu = MENU_GetMenu( hPtMenu );
2648 if( IS_SYSTEM_MENU(ptmenu) )
2649 id = 0;
2650 else
2651 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2654 if( id == NO_SELECTED_ITEM )
2656 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2657 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2660 else if( ptmenu->FocusedItem != id )
2662 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2663 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2665 return TRUE;
2669 /***********************************************************************
2670 * MENU_DoNextMenu
2672 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2674 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2676 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2677 BOOL atEnd = FALSE;
2679 /* When skipping left, we need to do something special after the
2680 first menu. */
2681 if (vk == VK_LEFT && menu->FocusedItem == 0)
2683 atEnd = TRUE;
2685 /* When skipping right, for the non-system menu, we need to
2686 handle the last non-special menu item (ie skip any window
2687 icons such as MDI maximize, restore or close) */
2688 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2690 UINT i = menu->FocusedItem + 1;
2691 while (i < menu->nItems) {
2692 if ((menu->items[i].wID >= SC_SIZE &&
2693 menu->items[i].wID <= SC_RESTORE)) {
2694 i++;
2695 } else break;
2697 if (i == menu->nItems) {
2698 atEnd = TRUE;
2701 /* When skipping right, we need to cater for the system menu */
2702 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2704 if (menu->FocusedItem == (menu->nItems - 1)) {
2705 atEnd = TRUE;
2709 if( atEnd )
2711 MDINEXTMENU next_menu;
2712 HMENU hNewMenu;
2713 HWND hNewWnd;
2714 UINT id = 0;
2716 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2717 next_menu.hmenuNext = 0;
2718 next_menu.hwndNext = 0;
2719 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2721 TRACE("%p [%p] -> %p [%p]\n",
2722 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2724 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2726 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2727 hNewWnd = pmt->hOwnerWnd;
2728 if( IS_SYSTEM_MENU(menu) )
2730 /* switch to the menu bar */
2732 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2734 if( vk == VK_LEFT )
2736 menu = MENU_GetMenu( hNewMenu );
2737 id = menu->nItems - 1;
2739 /* Skip backwards over any system predefined icons,
2740 eg. MDI close, restore etc icons */
2741 while ((id > 0) &&
2742 (menu->items[id].wID >= SC_SIZE &&
2743 menu->items[id].wID <= SC_RESTORE)) id--;
2746 else if (style & WS_SYSMENU )
2748 /* switch to the system menu */
2749 hNewMenu = get_win_sys_menu( hNewWnd );
2751 else return FALSE;
2753 else /* application returned a new menu to switch to */
2755 hNewMenu = next_menu.hmenuNext;
2756 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2758 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2760 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2762 if (style & WS_SYSMENU &&
2763 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2765 /* get the real system menu */
2766 hNewMenu = get_win_sys_menu(hNewWnd);
2768 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2770 /* FIXME: Not sure what to do here;
2771 * perhaps try to track hNewMenu as a popup? */
2773 TRACE(" -- got confused.\n");
2774 return FALSE;
2777 else return FALSE;
2780 if( hNewMenu != pmt->hTopMenu )
2782 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2783 FALSE, 0 );
2784 if( pmt->hCurrentMenu != pmt->hTopMenu )
2785 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2788 if( hNewWnd != pmt->hOwnerWnd )
2790 pmt->hOwnerWnd = hNewWnd;
2791 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2794 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2795 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2797 return TRUE;
2799 return FALSE;
2802 /***********************************************************************
2803 * MENU_SuspendPopup
2805 * The idea is not to show the popup if the next input message is
2806 * going to hide it anyway.
2808 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2810 MSG msg;
2812 msg.hwnd = pmt->hOwnerWnd;
2814 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2815 pmt->trackFlags |= TF_SKIPREMOVE;
2817 switch( uMsg )
2819 case WM_KEYDOWN:
2820 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2821 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2823 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2824 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2825 if( msg.message == WM_KEYDOWN &&
2826 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2828 pmt->trackFlags |= TF_SUSPENDPOPUP;
2829 return TRUE;
2832 break;
2835 /* failures go through this */
2836 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2837 return FALSE;
2840 /***********************************************************************
2841 * MENU_KeyEscape
2843 * Handle a VK_ESCAPE key event in a menu.
2845 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2847 BOOL bEndMenu = TRUE;
2849 if (pmt->hCurrentMenu != pmt->hTopMenu)
2851 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2853 if (menu->wFlags & MF_POPUP)
2855 HMENU hmenutmp, hmenuprev;
2857 hmenuprev = hmenutmp = pmt->hTopMenu;
2859 /* close topmost popup */
2860 while (hmenutmp != pmt->hCurrentMenu)
2862 hmenuprev = hmenutmp;
2863 hmenutmp = MENU_GetSubPopup( hmenuprev );
2866 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2867 pmt->hCurrentMenu = hmenuprev;
2868 bEndMenu = FALSE;
2872 return bEndMenu;
2875 /***********************************************************************
2876 * MENU_KeyLeft
2878 * Handle a VK_LEFT key event in a menu.
2880 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2882 POPUPMENU *menu;
2883 HMENU hmenutmp, hmenuprev;
2884 UINT prevcol;
2886 hmenuprev = hmenutmp = pmt->hTopMenu;
2887 menu = MENU_GetMenu( hmenutmp );
2889 /* Try to move 1 column left (if possible) */
2890 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2891 NO_SELECTED_ITEM ) {
2893 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2894 prevcol, TRUE, 0 );
2895 return;
2898 /* close topmost popup */
2899 while (hmenutmp != pmt->hCurrentMenu)
2901 hmenuprev = hmenutmp;
2902 hmenutmp = MENU_GetSubPopup( hmenuprev );
2905 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2906 pmt->hCurrentMenu = hmenuprev;
2908 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2910 /* move menu bar selection if no more popups are left */
2912 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2913 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2915 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2917 /* A sublevel menu was displayed - display the next one
2918 * unless there is another displacement coming up */
2920 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2921 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2922 pmt->hTopMenu, TRUE, wFlags);
2928 /***********************************************************************
2929 * MENU_KeyRight
2931 * Handle a VK_RIGHT key event in a menu.
2933 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2935 HMENU hmenutmp;
2936 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2937 UINT nextcol;
2939 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2940 pmt->hCurrentMenu,
2941 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2942 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2944 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2946 /* If already displaying a popup, try to display sub-popup */
2948 hmenutmp = pmt->hCurrentMenu;
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2951 /* if subpopup was displayed then we are done */
2952 if (hmenutmp != pmt->hCurrentMenu) return;
2955 /* Check to see if there's another column */
2956 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2957 NO_SELECTED_ITEM ) {
2958 TRACE("Going to %d.\n", nextcol );
2959 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2960 nextcol, TRUE, 0 );
2961 return;
2964 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2966 if( pmt->hCurrentMenu != pmt->hTopMenu )
2968 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2969 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2970 } else hmenutmp = 0;
2972 /* try to move to the next item */
2973 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2974 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2976 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2977 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2978 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2979 pmt->hTopMenu, TRUE, wFlags);
2983 static void CALLBACK release_capture( BOOL __normal )
2985 set_capture_window( 0, GUI_INMENUMODE, NULL );
2988 /***********************************************************************
2989 * MENU_TrackMenu
2991 * Menu tracking code.
2993 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2994 HWND hwnd, const RECT *lprect )
2996 MSG msg;
2997 POPUPMENU *menu;
2998 BOOL fRemove;
2999 INT executedMenuId = -1;
3000 MTRACKER mt;
3001 BOOL enterIdleSent = FALSE;
3002 HWND capture_win;
3004 mt.trackFlags = 0;
3005 mt.hCurrentMenu = hmenu;
3006 mt.hTopMenu = hmenu;
3007 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3008 mt.pt.x = x;
3009 mt.pt.y = y;
3011 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3012 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3014 fEndMenu = FALSE;
3015 if (!(menu = MENU_GetMenu( hmenu )))
3017 WARN("Invalid menu handle %p\n", hmenu);
3018 SetLastError(ERROR_INVALID_MENU_HANDLE);
3019 return FALSE;
3022 if (wFlags & TPM_BUTTONDOWN)
3024 /* Get the result in order to start the tracking or not */
3025 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3026 fEndMenu = !fRemove;
3029 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3031 /* owner may not be visible when tracking a popup, so use the menu itself */
3032 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3033 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3035 __TRY while (!fEndMenu)
3037 menu = MENU_GetMenu( mt.hCurrentMenu );
3038 if (!menu) /* sometimes happens if I do a window manager close */
3039 break;
3041 /* we have to keep the message in the queue until it's
3042 * clear that menu loop is not over yet. */
3044 for (;;)
3046 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3048 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3049 /* remove the message from the queue */
3050 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3052 else
3054 if (!enterIdleSent)
3056 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3057 enterIdleSent = TRUE;
3058 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3060 WaitMessage();
3064 /* check if EndMenu() tried to cancel us, by posting this message */
3065 if(msg.message == WM_CANCELMODE)
3067 /* we are now out of the loop */
3068 fEndMenu = TRUE;
3070 /* remove the message from the queue */
3071 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3073 /* break out of internal loop, ala ESCAPE */
3074 break;
3077 TranslateMessage( &msg );
3078 mt.pt = msg.pt;
3080 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3081 enterIdleSent=FALSE;
3083 fRemove = FALSE;
3084 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3087 * Use the mouse coordinates in lParam instead of those in the MSG
3088 * struct to properly handle synthetic messages. They are already
3089 * in screen coordinates.
3091 mt.pt.x = (short)LOWORD(msg.lParam);
3092 mt.pt.y = (short)HIWORD(msg.lParam);
3094 /* Find a menu for this mouse event */
3095 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3097 switch(msg.message)
3099 /* no WM_NC... messages in captured state */
3101 case WM_RBUTTONDBLCLK:
3102 case WM_RBUTTONDOWN:
3103 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3104 /* fall through */
3105 case WM_LBUTTONDBLCLK:
3106 case WM_LBUTTONDOWN:
3107 /* If the message belongs to the menu, removes it from the queue */
3108 /* Else, end menu tracking */
3109 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3110 fEndMenu = !fRemove;
3111 break;
3113 case WM_RBUTTONUP:
3114 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3115 /* fall through */
3116 case WM_LBUTTONUP:
3117 /* Check if a menu was selected by the mouse */
3118 if (hmenu)
3120 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3121 TRACE("executedMenuId %d\n", executedMenuId);
3123 /* End the loop if executedMenuId is an item ID */
3124 /* or if the job was done (executedMenuId = 0). */
3125 fEndMenu = fRemove = (executedMenuId != -1);
3127 /* No menu was selected by the mouse */
3128 /* if the function was called by TrackPopupMenu, continue
3129 with the menu tracking. If not, stop it */
3130 else
3131 fEndMenu = !(wFlags & TPM_POPUPMENU);
3133 break;
3135 case WM_MOUSEMOVE:
3136 /* the selected menu item must be changed every time */
3137 /* the mouse moves. */
3139 if (hmenu)
3140 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3142 } /* switch(msg.message) - mouse */
3144 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3146 fRemove = TRUE; /* Keyboard messages are always removed */
3147 switch(msg.message)
3149 case WM_KEYDOWN:
3150 case WM_SYSKEYDOWN:
3151 switch(msg.wParam)
3153 case VK_MENU:
3154 case VK_F10:
3155 fEndMenu = TRUE;
3156 break;
3158 case VK_HOME:
3159 case VK_END:
3160 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3161 NO_SELECTED_ITEM, FALSE, 0 );
3162 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3163 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3164 break;
3166 case VK_UP:
3167 case VK_DOWN: /* If on menu bar, pull-down the menu */
3169 menu = MENU_GetMenu( mt.hCurrentMenu );
3170 if (!(menu->wFlags & MF_POPUP))
3171 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3172 else /* otherwise try to move selection */
3173 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3174 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3175 break;
3177 case VK_LEFT:
3178 MENU_KeyLeft( &mt, wFlags );
3179 break;
3181 case VK_RIGHT:
3182 MENU_KeyRight( &mt, wFlags );
3183 break;
3185 case VK_ESCAPE:
3186 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3187 break;
3189 case VK_F1:
3191 HELPINFO hi;
3192 hi.cbSize = sizeof(HELPINFO);
3193 hi.iContextType = HELPINFO_MENUITEM;
3194 if (menu->FocusedItem == NO_SELECTED_ITEM)
3195 hi.iCtrlId = 0;
3196 else
3197 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3198 hi.hItemHandle = hmenu;
3199 hi.dwContextId = menu->dwContextHelpID;
3200 hi.MousePos = msg.pt;
3201 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3202 break;
3205 default:
3206 break;
3208 break; /* WM_KEYDOWN */
3210 case WM_CHAR:
3211 case WM_SYSCHAR:
3213 UINT pos;
3215 if (msg.wParam == '\r' || msg.wParam == ' ')
3217 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3218 fEndMenu = (executedMenuId != -2);
3220 break;
3223 /* Hack to avoid control chars. */
3224 /* We will find a better way real soon... */
3225 if (msg.wParam < 32) break;
3227 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3228 LOWORD(msg.wParam), FALSE );
3229 if (pos == (UINT)-2) fEndMenu = TRUE;
3230 else if (pos == (UINT)-1) MessageBeep(0);
3231 else
3233 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3234 TRUE, 0 );
3235 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3236 fEndMenu = (executedMenuId != -2);
3239 break;
3240 } /* switch(msg.message) - kbd */
3242 else
3244 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3245 DispatchMessageW( &msg );
3246 continue;
3249 if (!fEndMenu) fRemove = TRUE;
3251 /* finally remove message from the queue */
3253 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3254 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3255 else mt.trackFlags &= ~TF_SKIPREMOVE;
3257 __FINALLY( release_capture )
3259 /* If dropdown is still painted and the close box is clicked on
3260 then the menu will be destroyed as part of the DispatchMessage above.
3261 This will then invalidate the menu handle in mt.hTopMenu. We should
3262 check for this first. */
3263 if( IsMenu( mt.hTopMenu ) )
3265 menu = MENU_GetMenu( mt.hTopMenu );
3267 if( IsWindow( mt.hOwnerWnd ) )
3269 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3271 if (menu && (menu->wFlags & MF_POPUP))
3273 DestroyWindow( menu->hWnd );
3274 menu->hWnd = 0;
3276 if (!(wFlags & TPM_NONOTIFY))
3277 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3278 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3280 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3281 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3284 /* Reset the variable for hiding menu */
3285 if( menu ) menu->bTimeToHide = FALSE;
3288 /* The return value is only used by TrackPopupMenu */
3289 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3290 if (executedMenuId == -1) executedMenuId = 0;
3291 return executedMenuId;
3294 /***********************************************************************
3295 * MENU_InitTracking
3297 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3299 POPUPMENU *menu;
3301 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3303 HideCaret(0);
3305 /* This makes the menus of applications built with Delphi work.
3306 * It also enables menus to be displayed in more than one window,
3307 * but there are some bugs left that need to be fixed in this case.
3309 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3310 if (!top_popup) top_popup_hmenu = hMenu;
3312 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3313 if (!(wFlags & TPM_NONOTIFY))
3314 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3316 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3318 if (!(wFlags & TPM_NONOTIFY))
3320 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3321 /* If an app changed/recreated menu bar entries in WM_INITMENU
3322 * menu sizes will be recalculated once the menu created/shown.
3326 return TRUE;
3329 /***********************************************************************
3330 * MENU_ExitTracking
3332 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3334 TRACE("hwnd=%p\n", hWnd);
3336 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3337 ShowCaret(0);
3338 top_popup = 0;
3339 top_popup_hmenu = NULL;
3340 return TRUE;
3343 /***********************************************************************
3344 * MENU_TrackMouseMenuBar
3346 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3348 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3350 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3351 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3353 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3355 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3356 if (IsMenu(hMenu))
3358 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3359 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3360 MENU_ExitTracking(hWnd, FALSE);
3365 /***********************************************************************
3366 * MENU_TrackKbdMenuBar
3368 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3370 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3372 UINT uItem = NO_SELECTED_ITEM;
3373 HMENU hTrackMenu;
3374 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3376 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3378 /* find window that has a menu */
3380 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3381 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3383 /* check if we have to track a system menu */
3385 hTrackMenu = GetMenu( hwnd );
3386 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3388 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3389 hTrackMenu = get_win_sys_menu( hwnd );
3390 uItem = 0;
3391 wParam |= HTSYSMENU; /* prevent item lookup */
3393 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3395 if (!IsMenu( hTrackMenu )) return;
3397 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3399 if( wChar && wChar != ' ' )
3401 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3402 if ( uItem >= (UINT)(-2) )
3404 if( uItem == (UINT)(-1) ) MessageBeep(0);
3405 /* schedule end of menu tracking */
3406 wFlags |= TF_ENDMENU;
3407 goto track_menu;
3411 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3413 if (!(wParam & HTSYSMENU) || wChar == ' ')
3415 if( uItem == NO_SELECTED_ITEM )
3416 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3417 else
3418 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3421 track_menu:
3422 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3423 MENU_ExitTracking( hwnd, FALSE );
3426 /**********************************************************************
3427 * TrackPopupMenuEx (USER32.@)
3429 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3430 HWND hWnd, LPTPMPARAMS lpTpm )
3432 POPUPMENU *menu;
3433 BOOL ret = FALSE;
3435 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3436 hMenu, wFlags, x, y, hWnd, lpTpm,
3437 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3439 /* Parameter check */
3440 /* FIXME: this check is performed several times, here and in the called
3441 functions. That could be optimized */
3442 if (!(menu = MENU_GetMenu( hMenu )))
3444 SetLastError( ERROR_INVALID_MENU_HANDLE );
3445 return FALSE;
3448 if (IsWindow(menu->hWnd))
3450 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3451 return FALSE;
3454 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3456 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3457 if (!(wFlags & TPM_NONOTIFY))
3458 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3460 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3461 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3462 lpTpm ? &lpTpm->rcExclude : NULL );
3463 MENU_ExitTracking(hWnd, TRUE);
3465 return ret;
3468 /**********************************************************************
3469 * TrackPopupMenu (USER32.@)
3471 * Like the win32 API, the function return the command ID only if the
3472 * flag TPM_RETURNCMD is on.
3475 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3476 INT nReserved, HWND hWnd, const RECT *lpRect )
3478 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3481 /***********************************************************************
3482 * PopupMenuWndProc
3484 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3486 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3488 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3490 switch(message)
3492 case WM_CREATE:
3494 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3495 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3496 return 0;
3499 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3500 return MA_NOACTIVATE;
3502 case WM_PAINT:
3504 PAINTSTRUCT ps;
3505 BeginPaint( hwnd, &ps );
3506 MENU_DrawPopupMenu( hwnd, ps.hdc,
3507 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3508 EndPaint( hwnd, &ps );
3509 return 0;
3512 case WM_PRINTCLIENT:
3514 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3515 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3516 return 0;
3519 case WM_ERASEBKGND:
3520 return 1;
3522 case WM_DESTROY:
3523 /* zero out global pointer in case resident popup window was destroyed. */
3524 if (hwnd == top_popup) {
3525 top_popup = 0;
3526 top_popup_hmenu = NULL;
3528 break;
3530 case WM_SHOWWINDOW:
3532 if( wParam )
3534 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3536 else
3537 SetWindowLongPtrW( hwnd, 0, 0 );
3538 break;
3540 case MM_SETMENUHANDLE:
3541 SetWindowLongPtrW( hwnd, 0, wParam );
3542 break;
3544 case MM_GETMENUHANDLE:
3545 case MN_GETHMENU:
3546 return GetWindowLongPtrW( hwnd, 0 );
3548 default:
3549 return DefWindowProcW( hwnd, message, wParam, lParam );
3551 return 0;
3555 /***********************************************************************
3556 * MENU_GetMenuBarHeight
3558 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3560 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3561 INT orgX, INT orgY )
3563 HDC hdc;
3564 RECT rectBar;
3565 LPPOPUPMENU lppop;
3567 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3569 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3571 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3572 SelectObject( hdc, get_menu_font(FALSE));
3573 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3574 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3575 ReleaseDC( hwnd, hdc );
3576 return lppop->Height;
3580 /*******************************************************************
3581 * ChangeMenuA (USER32.@)
3583 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3584 UINT id, UINT flags )
3586 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3587 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3588 id, data );
3589 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3590 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3591 id, data );
3592 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3593 flags & MF_BYPOSITION ? pos : id,
3594 flags & ~MF_REMOVE );
3595 /* Default: MF_INSERT */
3596 return InsertMenuA( hMenu, pos, flags, id, data );
3600 /*******************************************************************
3601 * ChangeMenuW (USER32.@)
3603 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3604 UINT id, UINT flags )
3606 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3607 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3608 id, data );
3609 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3610 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3611 id, data );
3612 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3613 flags & MF_BYPOSITION ? pos : id,
3614 flags & ~MF_REMOVE );
3615 /* Default: MF_INSERT */
3616 return InsertMenuW( hMenu, pos, flags, id, data );
3620 /*******************************************************************
3621 * CheckMenuItem (USER32.@)
3623 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3625 MENUITEM *item;
3626 DWORD ret;
3628 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3629 ret = item->fState & MF_CHECKED;
3630 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3631 else item->fState &= ~MF_CHECKED;
3632 return ret;
3636 /**********************************************************************
3637 * EnableMenuItem (USER32.@)
3639 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3641 UINT oldflags;
3642 MENUITEM *item;
3643 POPUPMENU *menu;
3645 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3647 /* Get the Popupmenu to access the owner menu */
3648 if (!(menu = MENU_GetMenu(hMenu)))
3649 return (UINT)-1;
3651 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3652 return (UINT)-1;
3654 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3655 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3657 /* If the close item in the system menu change update the close button */
3658 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3660 if (menu->hSysMenuOwner != 0)
3662 RECT rc;
3663 POPUPMENU* parentMenu;
3665 /* Get the parent menu to access*/
3666 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3667 return (UINT)-1;
3669 /* Refresh the frame to reflect the change */
3670 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3671 rc.bottom = 0;
3672 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3676 return oldflags;
3680 /*******************************************************************
3681 * GetMenuStringA (USER32.@)
3683 INT WINAPI GetMenuStringA(
3684 HMENU hMenu, /* [in] menuhandle */
3685 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3686 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3687 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3688 UINT wFlags /* [in] MF_ flags */
3690 MENUITEM *item;
3692 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3693 if (str && nMaxSiz) str[0] = '\0';
3694 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3695 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3696 return 0;
3698 if (!item->text) return 0;
3699 if (!str || !nMaxSiz) return strlenW(item->text);
3700 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3701 str[nMaxSiz-1] = 0;
3702 TRACE("returning %s\n", debugstr_a(str));
3703 return strlen(str);
3707 /*******************************************************************
3708 * GetMenuStringW (USER32.@)
3710 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3711 LPWSTR str, INT nMaxSiz, UINT wFlags )
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 (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3722 if( !(item->text)) {
3723 str[0] = 0;
3724 return 0;
3726 lstrcpynW( str, item->text, nMaxSiz );
3727 TRACE("returning %s\n", debugstr_w(str));
3728 return strlenW(str);
3732 /**********************************************************************
3733 * HiliteMenuItem (USER32.@)
3735 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3736 UINT wHilite )
3738 LPPOPUPMENU menu;
3739 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3740 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3741 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3742 if (menu->FocusedItem == wItemID) return TRUE;
3743 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3744 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3745 return TRUE;
3749 /**********************************************************************
3750 * GetMenuState (USER32.@)
3752 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3754 MENUITEM *item;
3755 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3756 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3757 debug_print_menuitem (" item: ", item, "");
3758 if (item->fType & MF_POPUP)
3760 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3761 if (!menu) return -1;
3762 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3764 else
3766 /* We used to (from way back then) mask the result to 0xff. */
3767 /* I don't know why and it seems wrong as the documented */
3768 /* return flag MF_SEPARATOR is outside that mask. */
3769 return (item->fType | item->fState);
3774 /**********************************************************************
3775 * GetMenuItemCount (USER32.@)
3777 INT WINAPI GetMenuItemCount( HMENU hMenu )
3779 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3780 if (!menu) return -1;
3781 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3782 return menu->nItems;
3786 /**********************************************************************
3787 * GetMenuItemID (USER32.@)
3789 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3791 MENUITEM * lpmi;
3793 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3794 if (lpmi->fType & MF_POPUP) return -1;
3795 return lpmi->wID;
3800 /**********************************************************************
3801 * MENU_mnu2mnuii
3803 * Uses flags, id and text ptr, passed by InsertMenu() and
3804 * ModifyMenu() to setup a MenuItemInfo structure.
3806 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3807 LPMENUITEMINFOW pmii)
3809 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3810 pmii->cbSize = sizeof( MENUITEMINFOW);
3811 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3812 /* setting bitmap clears text and vice versa */
3813 if( IS_STRING_ITEM(flags)) {
3814 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3815 if( !str)
3816 flags |= MF_SEPARATOR;
3817 /* Item beginning with a backspace is a help item */
3818 /* FIXME: wrong place, this is only true in win16 */
3819 else if( *str == '\b') {
3820 flags |= MF_HELP;
3821 str++;
3823 pmii->dwTypeData = (LPWSTR)str;
3824 } else if( flags & MFT_BITMAP){
3825 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3826 pmii->hbmpItem = (HBITMAP)str;
3828 if( flags & MF_OWNERDRAW){
3829 pmii->fMask |= MIIM_DATA;
3830 pmii->dwItemData = (ULONG_PTR) str;
3832 if( flags & MF_POPUP) {
3833 pmii->fMask |= MIIM_SUBMENU;
3834 pmii->hSubMenu = (HMENU)id;
3836 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3837 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3838 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3839 pmii->wID = (UINT)id;
3843 /*******************************************************************
3844 * InsertMenuW (USER32.@)
3846 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3847 UINT_PTR id, LPCWSTR str )
3849 MENUITEM *item;
3850 MENUITEMINFOW mii;
3852 if (IS_STRING_ITEM(flags) && str)
3853 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3854 hMenu, pos, flags, id, debugstr_w(str) );
3855 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3856 hMenu, pos, flags, id, str );
3858 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3859 MENU_mnu2mnuii( flags, id, str, &mii);
3860 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3862 RemoveMenu( hMenu, pos, flags );
3863 return FALSE;
3866 item->hCheckBit = item->hUnCheckBit = 0;
3867 return TRUE;
3871 /*******************************************************************
3872 * InsertMenuA (USER32.@)
3874 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3875 UINT_PTR id, LPCSTR str )
3877 BOOL ret = FALSE;
3879 if (IS_STRING_ITEM(flags) && str)
3881 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3882 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3883 if (newstr)
3885 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3886 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3887 HeapFree( GetProcessHeap(), 0, newstr );
3889 return ret;
3891 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3895 /*******************************************************************
3896 * AppendMenuA (USER32.@)
3898 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3899 UINT_PTR id, LPCSTR data )
3901 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3905 /*******************************************************************
3906 * AppendMenuW (USER32.@)
3908 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3909 UINT_PTR id, LPCWSTR data )
3911 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3915 /**********************************************************************
3916 * RemoveMenu (USER32.@)
3918 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3920 LPPOPUPMENU menu;
3921 MENUITEM *item;
3923 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3924 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3925 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3927 /* Remove item */
3929 MENU_FreeItemData( item );
3931 if (--menu->nItems == 0)
3933 HeapFree( GetProcessHeap(), 0, menu->items );
3934 menu->items = NULL;
3936 else
3938 while(nPos < menu->nItems)
3940 *item = *(item+1);
3941 item++;
3942 nPos++;
3944 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3945 menu->nItems * sizeof(MENUITEM) );
3947 return TRUE;
3951 /**********************************************************************
3952 * DeleteMenu (USER32.@)
3954 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3956 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3957 if (!item) return FALSE;
3958 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3959 /* nPos is now the position of the item */
3960 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3961 return TRUE;
3965 /*******************************************************************
3966 * ModifyMenuW (USER32.@)
3968 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3969 UINT_PTR id, LPCWSTR str )
3971 MENUITEM *item;
3972 MENUITEMINFOW mii;
3974 if (IS_STRING_ITEM(flags))
3975 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3976 else
3977 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3979 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3980 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3981 MENU_mnu2mnuii( flags, id, str, &mii);
3982 return SetMenuItemInfo_common( item, &mii, TRUE);
3986 /*******************************************************************
3987 * ModifyMenuA (USER32.@)
3989 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3990 UINT_PTR id, LPCSTR str )
3992 BOOL ret = FALSE;
3994 if (IS_STRING_ITEM(flags) && str)
3996 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3997 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3998 if (newstr)
4000 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4001 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4002 HeapFree( GetProcessHeap(), 0, newstr );
4004 return ret;
4006 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4010 /**********************************************************************
4011 * CreatePopupMenu (USER32.@)
4013 HMENU WINAPI CreatePopupMenu(void)
4015 HMENU hmenu;
4016 POPUPMENU *menu;
4018 if (!(hmenu = CreateMenu())) return 0;
4019 menu = MENU_GetMenu( hmenu );
4020 menu->wFlags |= MF_POPUP;
4021 menu->bTimeToHide = FALSE;
4022 return hmenu;
4026 /**********************************************************************
4027 * GetMenuCheckMarkDimensions (USER.417)
4028 * GetMenuCheckMarkDimensions (USER32.@)
4030 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4032 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4036 /**********************************************************************
4037 * SetMenuItemBitmaps (USER32.@)
4039 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4040 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4042 MENUITEM *item;
4044 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4046 if (!hNewCheck && !hNewUnCheck)
4048 item->fState &= ~MF_USECHECKBITMAPS;
4050 else /* Install new bitmaps */
4052 item->hCheckBit = hNewCheck;
4053 item->hUnCheckBit = hNewUnCheck;
4054 item->fState |= MF_USECHECKBITMAPS;
4056 return TRUE;
4060 /**********************************************************************
4061 * CreateMenu (USER32.@)
4063 HMENU WINAPI CreateMenu(void)
4065 HMENU hMenu;
4066 LPPOPUPMENU menu;
4068 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4069 menu->FocusedItem = NO_SELECTED_ITEM;
4070 menu->bTimeToHide = FALSE;
4072 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4074 TRACE("return %p\n", hMenu );
4076 return hMenu;
4080 /**********************************************************************
4081 * DestroyMenu (USER32.@)
4083 BOOL WINAPI DestroyMenu( HMENU hMenu )
4085 LPPOPUPMENU lppop;
4087 TRACE("(%p)\n", hMenu);
4089 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4090 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4092 /* DestroyMenu should not destroy system menu popup owner */
4093 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4095 DestroyWindow( lppop->hWnd );
4096 lppop->hWnd = 0;
4099 if (lppop->items) /* recursively destroy submenus */
4101 int i;
4102 MENUITEM *item = lppop->items;
4103 for (i = lppop->nItems; i > 0; i--, item++)
4105 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4106 MENU_FreeItemData( item );
4108 HeapFree( GetProcessHeap(), 0, lppop->items );
4110 HeapFree( GetProcessHeap(), 0, lppop );
4111 return TRUE;
4115 /**********************************************************************
4116 * GetSystemMenu (USER32.@)
4118 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4120 WND *wndPtr = WIN_GetPtr( hWnd );
4121 HMENU retvalue = 0;
4123 if (wndPtr == WND_DESKTOP) return 0;
4124 if (wndPtr == WND_OTHER_PROCESS)
4126 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4128 else if (wndPtr)
4130 if (wndPtr->hSysMenu && bRevert)
4132 DestroyMenu(wndPtr->hSysMenu);
4133 wndPtr->hSysMenu = 0;
4136 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4137 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4139 if( wndPtr->hSysMenu )
4141 POPUPMENU *menu;
4142 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4144 /* Store the dummy sysmenu handle to facilitate the refresh */
4145 /* of the close button if the SC_CLOSE item change */
4146 menu = MENU_GetMenu(retvalue);
4147 if ( menu )
4148 menu->hSysMenuOwner = wndPtr->hSysMenu;
4150 WIN_ReleasePtr( wndPtr );
4152 return bRevert ? 0 : retvalue;
4156 /*******************************************************************
4157 * SetSystemMenu (USER32.@)
4159 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4161 WND *wndPtr = WIN_GetPtr( hwnd );
4163 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4165 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4166 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4167 WIN_ReleasePtr( wndPtr );
4168 return TRUE;
4170 return FALSE;
4174 /**********************************************************************
4175 * GetMenu (USER32.@)
4177 HMENU WINAPI GetMenu( HWND hWnd )
4179 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4180 TRACE("for %p returning %p\n", hWnd, retvalue);
4181 return retvalue;
4184 /**********************************************************************
4185 * GetMenuBarInfo (USER32.@)
4187 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4189 POPUPMENU *menu;
4190 HMENU hmenu = NULL;
4191 ATOM class_atom;
4193 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4195 switch (idObject)
4197 case OBJID_CLIENT:
4198 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4199 if (!class_atom)
4200 return FALSE;
4201 if (class_atom != POPUPMENU_CLASS_ATOM)
4203 WARN("called on invalid window: %d\n", class_atom);
4204 SetLastError(ERROR_INVALID_MENU_HANDLE);
4205 return FALSE;
4208 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4209 break;
4210 case OBJID_MENU:
4211 hmenu = GetMenu(hwnd);
4212 break;
4213 case OBJID_SYSMENU:
4214 hmenu = GetSystemMenu(hwnd, FALSE);
4215 break;
4216 default:
4217 return FALSE;
4220 if (!hmenu)
4221 return FALSE;
4223 if (pmbi->cbSize != sizeof(MENUBARINFO))
4225 SetLastError(ERROR_INVALID_PARAMETER);
4226 return FALSE;
4229 menu = MENU_GetMenu(hmenu);
4230 if (!menu)
4231 return FALSE;
4232 if (idItem < 0 || idItem > menu->nItems)
4233 return FALSE;
4235 if (!menu->Height)
4237 SetRectEmpty(&pmbi->rcBar);
4239 else if (idItem == 0)
4241 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4242 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4243 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4245 else
4247 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4250 pmbi->hMenu = hmenu;
4251 pmbi->hwndMenu = NULL;
4252 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4253 if (idItem)
4255 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4256 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4258 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4259 if (menu)
4260 pmbi->hwndMenu = menu->hWnd;
4263 else
4265 pmbi->fFocused = pmbi->fBarFocused;
4268 return TRUE;
4271 /**********************************************************************
4272 * MENU_SetMenu
4274 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4275 * SetWindowPos call that would result if SetMenu were called directly.
4277 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4279 TRACE("(%p, %p);\n", hWnd, hMenu);
4281 if (hMenu && !IsMenu(hMenu))
4283 WARN("hMenu %p is not a menu handle\n", hMenu);
4284 return FALSE;
4286 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4287 return FALSE;
4289 hWnd = WIN_GetFullHandle( hWnd );
4290 if (GetCapture() == hWnd)
4291 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4293 if (hMenu != 0)
4295 LPPOPUPMENU lpmenu;
4297 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4299 lpmenu->hWnd = hWnd;
4300 lpmenu->Height = 0; /* Make sure we recalculate the size */
4302 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4303 return TRUE;
4307 /**********************************************************************
4308 * SetMenu (USER32.@)
4310 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4312 if(!MENU_SetMenu(hWnd, hMenu))
4313 return FALSE;
4315 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4316 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4317 return TRUE;
4321 /**********************************************************************
4322 * GetSubMenu (USER32.@)
4324 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4326 MENUITEM * lpmi;
4328 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4329 if (!(lpmi->fType & MF_POPUP)) return 0;
4330 return lpmi->hSubMenu;
4334 /**********************************************************************
4335 * DrawMenuBar (USER32.@)
4337 BOOL WINAPI DrawMenuBar( HWND hWnd )
4339 LPPOPUPMENU lppop;
4340 HMENU hMenu = GetMenu(hWnd);
4342 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4343 return FALSE;
4344 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4346 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4347 lppop->hwndOwner = hWnd;
4348 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4349 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4350 return TRUE;
4353 /***********************************************************************
4354 * DrawMenuBarTemp (USER32.@)
4356 * UNDOCUMENTED !!
4358 * called by W98SE desk.cpl Control Panel Applet
4360 * Not 100% sure about the param names, but close.
4362 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4364 LPPOPUPMENU lppop;
4365 UINT i,retvalue;
4366 HFONT hfontOld = 0;
4367 BOOL flat_menu = FALSE;
4369 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4371 if (!hMenu)
4372 hMenu = GetMenu(hwnd);
4374 if (!hFont)
4375 hFont = get_menu_font(FALSE);
4377 lppop = MENU_GetMenu( hMenu );
4378 if (lppop == NULL || lprect == NULL)
4380 retvalue = GetSystemMetrics(SM_CYMENU);
4381 goto END;
4384 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4386 hfontOld = SelectObject( hDC, hFont);
4388 if (lppop->Height == 0)
4389 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4391 lprect->bottom = lprect->top + lppop->Height;
4393 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4395 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4396 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4397 LineTo( hDC, lprect->right, lprect->bottom );
4399 if (lppop->nItems == 0)
4401 retvalue = GetSystemMetrics(SM_CYMENU);
4402 goto END;
4405 for (i = 0; i < lppop->nItems; i++)
4407 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4408 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4410 retvalue = lppop->Height;
4412 END:
4413 if (hfontOld) SelectObject (hDC, hfontOld);
4414 return retvalue;
4417 /***********************************************************************
4418 * EndMenu (USER.187)
4419 * EndMenu (USER32.@)
4421 BOOL WINAPI EndMenu(void)
4423 /* if we are in the menu code, and it is active */
4424 if (!fEndMenu && top_popup)
4426 /* terminate the menu handling code */
4427 fEndMenu = TRUE;
4429 /* needs to be posted to wakeup the internal menu handler */
4430 /* which will now terminate the menu, in the event that */
4431 /* the main window was minimized, or lost focus, so we */
4432 /* don't end up with an orphaned menu */
4433 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4435 return fEndMenu;
4439 /*****************************************************************
4440 * LoadMenuA (USER32.@)
4442 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4444 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4445 if (!hrsrc) return 0;
4446 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4450 /*****************************************************************
4451 * LoadMenuW (USER32.@)
4453 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4455 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4456 if (!hrsrc) return 0;
4457 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4461 /**********************************************************************
4462 * LoadMenuIndirectW (USER32.@)
4464 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4466 HMENU hMenu;
4467 WORD version, offset;
4468 LPCSTR p = template;
4470 version = GET_WORD(p);
4471 p += sizeof(WORD);
4472 TRACE("%p, ver %d\n", template, version );
4473 switch (version)
4475 case 0: /* standard format is version of 0 */
4476 offset = GET_WORD(p);
4477 p += sizeof(WORD) + offset;
4478 if (!(hMenu = CreateMenu())) return 0;
4479 if (!MENU_ParseResource( p, hMenu ))
4481 DestroyMenu( hMenu );
4482 return 0;
4484 return hMenu;
4485 case 1: /* extended format is version of 1 */
4486 offset = GET_WORD(p);
4487 p += sizeof(WORD) + offset;
4488 if (!(hMenu = CreateMenu())) return 0;
4489 if (!MENUEX_ParseResource( p, hMenu))
4491 DestroyMenu( hMenu );
4492 return 0;
4494 return hMenu;
4495 default:
4496 ERR("version %d not supported.\n", version);
4497 return 0;
4502 /**********************************************************************
4503 * LoadMenuIndirectA (USER32.@)
4505 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4507 return LoadMenuIndirectW( template );
4511 /**********************************************************************
4512 * IsMenu (USER32.@)
4514 BOOL WINAPI IsMenu(HMENU hmenu)
4516 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4518 if (!menu)
4520 SetLastError(ERROR_INVALID_MENU_HANDLE);
4521 return FALSE;
4523 return TRUE;
4526 /**********************************************************************
4527 * GetMenuItemInfo_common
4530 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4531 LPMENUITEMINFOW lpmii, BOOL unicode)
4533 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4535 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4537 if (!menu) {
4538 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4539 return FALSE;
4542 if( lpmii->fMask & MIIM_TYPE) {
4543 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4544 WARN("invalid combination of fMask bits used\n");
4545 /* this does not happen on Win9x/ME */
4546 SetLastError( ERROR_INVALID_PARAMETER);
4547 return FALSE;
4549 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4550 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4551 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4552 if( lpmii->fType & MFT_BITMAP) {
4553 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4554 lpmii->cch = 0;
4555 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4556 /* this does not happen on Win9x/ME */
4557 lpmii->dwTypeData = 0;
4558 lpmii->cch = 0;
4562 /* copy the text string */
4563 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4564 if( !menu->text ) {
4565 if(lpmii->dwTypeData && lpmii->cch) {
4566 lpmii->cch = 0;
4567 if( unicode)
4568 *((WCHAR *)lpmii->dwTypeData) = 0;
4569 else
4570 *((CHAR *)lpmii->dwTypeData) = 0;
4572 } else {
4573 int len;
4574 if (unicode)
4576 len = strlenW(menu->text);
4577 if(lpmii->dwTypeData && lpmii->cch)
4578 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4580 else
4582 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4583 0, NULL, NULL ) - 1;
4584 if(lpmii->dwTypeData && lpmii->cch)
4585 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4586 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4587 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4589 /* if we've copied a substring we return its length */
4590 if(lpmii->dwTypeData && lpmii->cch)
4591 if (lpmii->cch <= len + 1)
4592 lpmii->cch--;
4593 else
4594 lpmii->cch = len;
4595 else {
4596 /* return length of string */
4597 /* not on Win9x/ME if fType & MFT_BITMAP */
4598 lpmii->cch = len;
4603 if (lpmii->fMask & MIIM_FTYPE)
4604 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4606 if (lpmii->fMask & MIIM_BITMAP)
4607 lpmii->hbmpItem = menu->hbmpItem;
4609 if (lpmii->fMask & MIIM_STATE)
4610 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4612 if (lpmii->fMask & MIIM_ID)
4613 lpmii->wID = menu->wID;
4615 if (lpmii->fMask & MIIM_SUBMENU)
4616 lpmii->hSubMenu = menu->hSubMenu;
4617 else {
4618 /* hSubMenu is always cleared
4619 * (not on Win9x/ME ) */
4620 lpmii->hSubMenu = 0;
4623 if (lpmii->fMask & MIIM_CHECKMARKS) {
4624 lpmii->hbmpChecked = menu->hCheckBit;
4625 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4627 if (lpmii->fMask & MIIM_DATA)
4628 lpmii->dwItemData = menu->dwItemData;
4630 return TRUE;
4633 /**********************************************************************
4634 * GetMenuItemInfoA (USER32.@)
4636 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4637 LPMENUITEMINFOA lpmii)
4639 BOOL ret;
4640 MENUITEMINFOA mii;
4641 if( lpmii->cbSize != sizeof( mii) &&
4642 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4643 SetLastError( ERROR_INVALID_PARAMETER);
4644 return FALSE;
4646 memcpy( &mii, lpmii, lpmii->cbSize);
4647 mii.cbSize = sizeof( mii);
4648 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4649 (LPMENUITEMINFOW)&mii, FALSE);
4650 mii.cbSize = lpmii->cbSize;
4651 memcpy( lpmii, &mii, mii.cbSize);
4652 return ret;
4655 /**********************************************************************
4656 * GetMenuItemInfoW (USER32.@)
4658 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4659 LPMENUITEMINFOW lpmii)
4661 BOOL ret;
4662 MENUITEMINFOW mii;
4663 if( lpmii->cbSize != sizeof( mii) &&
4664 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4665 SetLastError( ERROR_INVALID_PARAMETER);
4666 return FALSE;
4668 memcpy( &mii, lpmii, lpmii->cbSize);
4669 mii.cbSize = sizeof( mii);
4670 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4671 mii.cbSize = lpmii->cbSize;
4672 memcpy( lpmii, &mii, mii.cbSize);
4673 return ret;
4677 /* set a menu item text from a ASCII or Unicode string */
4678 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4680 if (!text)
4681 menu->text = NULL;
4682 else if (unicode)
4684 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4685 strcpyW( menu->text, text );
4687 else
4689 LPCSTR str = (LPCSTR)text;
4690 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4691 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4692 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4697 /**********************************************************************
4698 * MENU_depth
4700 * detect if there are loops in the menu tree (or the depth is too large)
4702 static int MENU_depth( POPUPMENU *pmenu, int depth)
4704 int i;
4705 MENUITEM *item;
4706 int subdepth;
4708 depth++;
4709 if( depth > MAXMENUDEPTH) return depth;
4710 item = pmenu->items;
4711 subdepth = depth;
4712 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4713 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4714 if( psubmenu){
4715 int bdepth = MENU_depth( psubmenu, depth);
4716 if( bdepth > subdepth) subdepth = bdepth;
4718 if( subdepth > MAXMENUDEPTH)
4719 TRACE("<- hmenu %p\n", item->hSubMenu);
4721 return subdepth;
4725 /**********************************************************************
4726 * SetMenuItemInfo_common
4728 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4729 * MIIM_BITMAP and MIIM_STRING flags instead.
4732 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4733 const MENUITEMINFOW *lpmii,
4734 BOOL unicode)
4736 if (!menu) return FALSE;
4738 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4740 if (lpmii->fMask & MIIM_FTYPE ) {
4741 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4742 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4744 if (lpmii->fMask & MIIM_STRING ) {
4745 /* free the string when used */
4746 HeapFree(GetProcessHeap(), 0, menu->text);
4747 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4750 if (lpmii->fMask & MIIM_STATE)
4751 /* Other menu items having MFS_DEFAULT are not converted
4752 to normal items */
4753 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4755 if (lpmii->fMask & MIIM_ID)
4756 menu->wID = lpmii->wID;
4758 if (lpmii->fMask & MIIM_SUBMENU) {
4759 menu->hSubMenu = lpmii->hSubMenu;
4760 if (menu->hSubMenu) {
4761 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4762 if (subMenu) {
4763 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4764 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4765 menu->hSubMenu = 0;
4766 return FALSE;
4768 subMenu->wFlags |= MF_POPUP;
4769 menu->fType |= MF_POPUP;
4770 } else {
4771 SetLastError( ERROR_INVALID_PARAMETER);
4772 return FALSE;
4775 else
4776 menu->fType &= ~MF_POPUP;
4779 if (lpmii->fMask & MIIM_CHECKMARKS)
4781 menu->hCheckBit = lpmii->hbmpChecked;
4782 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4784 if (lpmii->fMask & MIIM_DATA)
4785 menu->dwItemData = lpmii->dwItemData;
4787 if (lpmii->fMask & MIIM_BITMAP)
4788 menu->hbmpItem = lpmii->hbmpItem;
4790 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4791 menu->fType |= MFT_SEPARATOR;
4793 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4794 return TRUE;
4797 /**********************************************************************
4798 * MENU_NormalizeMenuItemInfoStruct
4800 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4801 * check, copy and extend the MENUITEMINFO struct from the version that the application
4802 * supplied to the version used by wine source. */
4803 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4804 MENUITEMINFOW *pmii_out )
4806 /* do we recognize the size? */
4807 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4808 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4809 SetLastError( ERROR_INVALID_PARAMETER);
4810 return FALSE;
4812 /* copy the fields that we have */
4813 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4814 /* if the hbmpItem member is missing then extend */
4815 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4816 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4817 pmii_out->hbmpItem = NULL;
4819 /* test for invalid bit combinations */
4820 if( (pmii_out->fMask & MIIM_TYPE &&
4821 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4822 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4823 WARN("invalid combination of fMask bits used\n");
4824 /* this does not happen on Win9x/ME */
4825 SetLastError( ERROR_INVALID_PARAMETER);
4826 return FALSE;
4828 /* convert old style (MIIM_TYPE) to the new */
4829 if( pmii_out->fMask & MIIM_TYPE){
4830 pmii_out->fMask |= MIIM_FTYPE;
4831 if( IS_STRING_ITEM(pmii_out->fType)){
4832 pmii_out->fMask |= MIIM_STRING;
4833 } else if( (pmii_out->fType) & MFT_BITMAP){
4834 pmii_out->fMask |= MIIM_BITMAP;
4835 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4838 return TRUE;
4841 /**********************************************************************
4842 * SetMenuItemInfoA (USER32.@)
4844 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4845 const MENUITEMINFOA *lpmii)
4847 MENUITEMINFOW mii;
4849 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4851 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4853 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4854 &mii, FALSE);
4857 /**********************************************************************
4858 * SetMenuItemInfoW (USER32.@)
4860 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4861 const MENUITEMINFOW *lpmii)
4863 MENUITEMINFOW mii;
4865 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4867 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4868 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4869 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4872 /**********************************************************************
4873 * SetMenuDefaultItem (USER32.@)
4876 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4878 UINT i;
4879 POPUPMENU *menu;
4880 MENUITEM *item;
4882 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4884 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4886 /* reset all default-item flags */
4887 item = menu->items;
4888 for (i = 0; i < menu->nItems; i++, item++)
4890 item->fState &= ~MFS_DEFAULT;
4893 /* no default item */
4894 if ( -1 == uItem)
4896 return TRUE;
4899 item = menu->items;
4900 if ( bypos )
4902 if ( uItem >= menu->nItems ) return FALSE;
4903 item[uItem].fState |= MFS_DEFAULT;
4904 return TRUE;
4906 else
4908 for (i = 0; i < menu->nItems; i++, item++)
4910 if (item->wID == uItem)
4912 item->fState |= MFS_DEFAULT;
4913 return TRUE;
4918 return FALSE;
4921 /**********************************************************************
4922 * GetMenuDefaultItem (USER32.@)
4924 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4926 POPUPMENU *menu;
4927 MENUITEM * item;
4928 UINT i = 0;
4930 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4932 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4934 /* find default item */
4935 item = menu->items;
4937 /* empty menu */
4938 if (! item) return -1;
4940 while ( !( item->fState & MFS_DEFAULT ) )
4942 i++; item++;
4943 if (i >= menu->nItems ) return -1;
4946 /* default: don't return disabled items */
4947 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4949 /* search rekursiv when needed */
4950 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4952 UINT ret;
4953 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4954 if ( -1 != ret ) return ret;
4956 /* when item not found in submenu, return the popup item */
4958 return ( bypos ) ? i : item->wID;
4963 /**********************************************************************
4964 * InsertMenuItemA (USER32.@)
4966 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4967 const MENUITEMINFOA *lpmii)
4969 MENUITEM *item;
4970 MENUITEMINFOW mii;
4972 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4974 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4976 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4977 return SetMenuItemInfo_common(item, &mii, FALSE);
4981 /**********************************************************************
4982 * InsertMenuItemW (USER32.@)
4984 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4985 const MENUITEMINFOW *lpmii)
4987 MENUITEM *item;
4988 MENUITEMINFOW mii;
4990 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4992 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4994 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4995 return SetMenuItemInfo_common(item, &mii, TRUE);
4998 /**********************************************************************
4999 * CheckMenuRadioItem (USER32.@)
5002 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5003 UINT first, UINT last, UINT check,
5004 UINT bypos)
5006 BOOL done = FALSE;
5007 UINT i;
5008 MENUITEM *mi_first = NULL, *mi_check;
5009 HMENU m_first, m_check;
5011 for (i = first; i <= last; i++)
5013 UINT pos = i;
5015 if (!mi_first)
5017 m_first = hMenu;
5018 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5019 if (!mi_first) continue;
5020 mi_check = mi_first;
5021 m_check = m_first;
5023 else
5025 m_check = hMenu;
5026 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5027 if (!mi_check) continue;
5030 if (m_first != m_check) continue;
5031 if (mi_check->fType == MFT_SEPARATOR) continue;
5033 if (i == check)
5035 mi_check->fType |= MFT_RADIOCHECK;
5036 mi_check->fState |= MFS_CHECKED;
5037 done = TRUE;
5039 else
5041 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5042 mi_check->fState &= ~MFS_CHECKED;
5046 return done;
5050 /**********************************************************************
5051 * GetMenuItemRect (USER32.@)
5053 * ATTENTION: Here, the returned values in rect are the screen
5054 * coordinates of the item just like if the menu was
5055 * always on the upper left side of the application.
5058 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5059 LPRECT rect)
5061 POPUPMENU *itemMenu;
5062 MENUITEM *item;
5063 HWND referenceHwnd;
5065 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5067 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5068 referenceHwnd = hwnd;
5070 if(!hwnd)
5072 itemMenu = MENU_GetMenu(hMenu);
5073 if (itemMenu == NULL)
5074 return FALSE;
5076 if(itemMenu->hWnd == 0)
5077 return FALSE;
5078 referenceHwnd = itemMenu->hWnd;
5081 if ((rect == NULL) || (item == NULL))
5082 return FALSE;
5084 *rect = item->rect;
5086 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5088 return TRUE;
5091 /**********************************************************************
5092 * SetMenuInfo (USER32.@)
5094 * FIXME
5095 * actually use the items to draw the menu
5096 * (recalculate and/or redraw)
5098 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5100 POPUPMENU *menu;
5101 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5103 if (lpmi->fMask & MIM_BACKGROUND)
5104 menu->hbrBack = lpmi->hbrBack;
5106 if (lpmi->fMask & MIM_HELPID)
5107 menu->dwContextHelpID = lpmi->dwContextHelpID;
5109 if (lpmi->fMask & MIM_MAXHEIGHT)
5110 menu->cyMax = lpmi->cyMax;
5112 if (lpmi->fMask & MIM_MENUDATA)
5113 menu->dwMenuData = lpmi->dwMenuData;
5115 if (lpmi->fMask & MIM_STYLE)
5116 menu->dwStyle = lpmi->dwStyle;
5118 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5119 int i;
5120 MENUITEM *item = menu->items;
5121 for( i = menu->nItems; i; i--, item++)
5122 if( item->fType & MF_POPUP)
5123 menu_SetMenuInfo( item->hSubMenu, lpmi);
5125 return TRUE;
5128 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5130 TRACE("(%p %p)\n", hMenu, lpmi);
5131 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5132 if( lpmi->fMask & MIM_STYLE) {
5133 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5134 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5135 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5137 return TRUE;
5139 SetLastError( ERROR_INVALID_PARAMETER);
5140 return FALSE;
5143 /**********************************************************************
5144 * GetMenuInfo (USER32.@)
5146 * NOTES
5147 * win98/NT5.0
5150 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5151 { POPUPMENU *menu;
5153 TRACE("(%p %p)\n", hMenu, lpmi);
5155 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5158 if (lpmi->fMask & MIM_BACKGROUND)
5159 lpmi->hbrBack = menu->hbrBack;
5161 if (lpmi->fMask & MIM_HELPID)
5162 lpmi->dwContextHelpID = menu->dwContextHelpID;
5164 if (lpmi->fMask & MIM_MAXHEIGHT)
5165 lpmi->cyMax = menu->cyMax;
5167 if (lpmi->fMask & MIM_MENUDATA)
5168 lpmi->dwMenuData = menu->dwMenuData;
5170 if (lpmi->fMask & MIM_STYLE)
5171 lpmi->dwStyle = menu->dwStyle;
5173 return TRUE;
5175 SetLastError( ERROR_INVALID_PARAMETER);
5176 return FALSE;
5180 /**********************************************************************
5181 * SetMenuContextHelpId (USER32.@)
5183 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5185 LPPOPUPMENU menu;
5187 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5189 if ((menu = MENU_GetMenu(hMenu)))
5191 menu->dwContextHelpID = dwContextHelpID;
5192 return TRUE;
5194 return FALSE;
5198 /**********************************************************************
5199 * GetMenuContextHelpId (USER32.@)
5201 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5203 LPPOPUPMENU menu;
5205 TRACE("(%p)\n", hMenu);
5207 if ((menu = MENU_GetMenu(hMenu)))
5209 return menu->dwContextHelpID;
5211 return 0;
5214 /**********************************************************************
5215 * MenuItemFromPoint (USER32.@)
5217 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5219 POPUPMENU *menu = MENU_GetMenu(hMenu);
5220 UINT pos;
5222 /*FIXME: Do we have to handle hWnd here? */
5223 if (!menu) return -1;
5224 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5225 return pos;
5229 /**********************************************************************
5230 * translate_accelerator
5232 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5233 BYTE fVirt, WORD key, WORD cmd )
5235 INT mask = 0;
5236 UINT mesg = 0;
5238 if (wParam != key) return FALSE;
5240 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5241 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5242 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5244 if (message == WM_CHAR || message == WM_SYSCHAR)
5246 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5248 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5249 goto found;
5252 else
5254 if(fVirt & FVIRTKEY)
5256 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5257 wParam, 0xff & HIWORD(lParam));
5259 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5260 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5262 else
5264 if (!(lParam & 0x01000000)) /* no special_key */
5266 if ((fVirt & FALT) && (lParam & 0x20000000))
5267 { /* ^^ ALT pressed */
5268 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5269 goto found;
5274 return FALSE;
5276 found:
5277 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5278 mesg = 1;
5279 else
5281 HMENU hMenu, hSubMenu, hSysMenu;
5282 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5284 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5285 hSysMenu = get_win_sys_menu( hWnd );
5287 /* find menu item and ask application to initialize it */
5288 /* 1. in the system menu */
5289 hSubMenu = hSysMenu;
5290 nPos = cmd;
5291 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5293 if (GetCapture())
5294 mesg = 2;
5295 if (!IsWindowEnabled(hWnd))
5296 mesg = 3;
5297 else
5299 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5300 if(hSubMenu != hSysMenu)
5302 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5303 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5304 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5306 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5309 else /* 2. in the window's menu */
5311 hSubMenu = hMenu;
5312 nPos = cmd;
5313 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5315 if (GetCapture())
5316 mesg = 2;
5317 if (!IsWindowEnabled(hWnd))
5318 mesg = 3;
5319 else
5321 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5322 if(hSubMenu != hMenu)
5324 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5325 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5326 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5328 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5333 if (mesg == 0)
5335 if (uSysStat != (UINT)-1)
5337 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5338 mesg=4;
5339 else
5340 mesg=WM_SYSCOMMAND;
5342 else
5344 if (uStat != (UINT)-1)
5346 if (IsIconic(hWnd))
5347 mesg=5;
5348 else
5350 if (uStat & (MF_DISABLED|MF_GRAYED))
5351 mesg=6;
5352 else
5353 mesg=WM_COMMAND;
5356 else
5357 mesg=WM_COMMAND;
5362 if( mesg==WM_COMMAND )
5364 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5365 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5367 else if( mesg==WM_SYSCOMMAND )
5369 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5370 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5372 else
5374 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5375 * #0: unknown (please report!)
5376 * #1: for WM_KEYUP,WM_SYSKEYUP
5377 * #2: mouse is captured
5378 * #3: window is disabled
5379 * #4: it's a disabled system menu option
5380 * #5: it's a menu option, but window is iconic
5381 * #6: it's a menu option, but disabled
5383 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5384 if(mesg==0)
5385 ERR_(accel)(" unknown reason - please report!\n");
5387 return TRUE;
5390 /**********************************************************************
5391 * TranslateAcceleratorA (USER32.@)
5392 * TranslateAccelerator (USER32.@)
5394 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5396 switch (msg->message)
5398 case WM_KEYDOWN:
5399 case WM_SYSKEYDOWN:
5400 return TranslateAcceleratorW( hWnd, hAccel, msg );
5402 case WM_CHAR:
5403 case WM_SYSCHAR:
5405 MSG msgW = *msg;
5406 char ch = LOWORD(msg->wParam);
5407 WCHAR wch;
5408 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5409 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5410 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5413 default:
5414 return 0;
5418 /**********************************************************************
5419 * TranslateAcceleratorW (USER32.@)
5421 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5423 ACCEL data[32], *ptr = data;
5424 int i, count;
5426 if (!hWnd) return 0;
5428 if (msg->message != WM_KEYDOWN &&
5429 msg->message != WM_SYSKEYDOWN &&
5430 msg->message != WM_CHAR &&
5431 msg->message != WM_SYSCHAR)
5432 return 0;
5434 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5435 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5437 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5438 if (count > sizeof(data)/sizeof(data[0]))
5440 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5442 count = CopyAcceleratorTableW( hAccel, ptr, count );
5443 for (i = 0; i < count; i++)
5445 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5446 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5447 break;
5449 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5450 return (i < count);