user32: Use BOOL type where appropriate.
[wine.git] / dlls / user32 / menu.c
blob50545eef42cb6b88720f94559f15497cb8de3651
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class =
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 const char *postfix)
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
229 if (mp) {
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
232 if ( mp->hSubMenu)
233 TRACE( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 TRACE( ", fType=");
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 if (flags)
248 TRACE( "+0x%x", flags);
250 flags = mp->fState;
251 if (flags) {
252 int count = 0;
253 TRACE( ", State=");
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
261 if (flags)
262 TRACE( "+0x%x", flags);
264 if (mp->hCheckBit)
265 TRACE( ", Chk=%p", mp->hCheckBit);
266 if (mp->hUnCheckBit)
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 if (mp->text)
269 TRACE( ", Text=%s", debugstr_w(mp->text));
270 if (mp->dwItemData)
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if (mp->hbmpItem)
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 else
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
279 TRACE( " }");
280 } else
281 TRACE( "NULL");
282 TRACE(" %s\n", postfix);
285 #undef MENUOUT
286 #undef MENUFLAG
289 /***********************************************************************
290 * MENU_GetMenu
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
298 if (menu == OBJ_OTHER_PROCESS)
300 WARN( "other process menu %p?\n", hMenu);
301 return NULL;
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
305 return menu;
308 /***********************************************************************
309 * get_win_sys_menu
311 * Get the system menu of a window
313 static HMENU get_win_sys_menu( HWND hwnd )
315 HMENU ret = 0;
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
319 ret = win->hSysMenu;
320 WIN_ReleasePtr( win );
322 return ret;
325 /***********************************************************************
326 * get_menu_font
328 static HFONT get_menu_font( BOOL bold )
330 static HFONT hMenuFont, hMenuFontBold;
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
334 if (!ret)
336 NONCLIENTMETRICSW ncm;
337 HFONT prev;
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
342 if (bold)
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 ret, NULL );
350 if (prev)
352 /* another thread beat us to it */
353 DeleteObject( ret );
354 ret = prev;
357 return ret;
360 /***********************************************************************
361 * get_arrow_bitmap
363 static HBITMAP get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap;
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 return arrow_bitmap;
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap;
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
379 return arrow_bitmap;
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap;
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
390 return arrow_bitmap;
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap;
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
401 return arrow_bitmap;
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap;
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
412 return arrow_bitmap;
415 /***********************************************************************
416 * MENU_CopySysPopup
418 * Return the default system menu.
420 static HMENU MENU_CopySysPopup(BOOL mdi)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
426 if( hMenu ) {
427 MENUINFO minfo;
428 MENUITEMINFOW miteminfo;
429 POPUPMENU* menu = MENU_GetMenu(hMenu);
430 menu->wFlags |= MF_SYSMENU | MF_POPUP;
431 /* decorate the menu with bitmaps */
432 minfo.cbSize = sizeof( MENUINFO);
433 minfo.dwStyle = MNS_CHECKORBMP;
434 minfo.fMask = MIM_STYLE;
435 SetMenuInfo( hMenu, &minfo);
436 miteminfo.cbSize = sizeof( MENUITEMINFOW);
437 miteminfo.fMask = MIIM_BITMAP;
438 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
439 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
440 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
441 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
443 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
446 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
448 else
449 ERR("Unable to load default system menu\n" );
451 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
453 return hMenu;
457 /**********************************************************************
458 * MENU_GetSysMenu
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
466 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
468 HMENU hMenu;
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
471 if ((hMenu = CreateMenu()))
473 POPUPMENU *menu = MENU_GetMenu(hMenu);
474 menu->wFlags = MF_SYSMENU;
475 menu->hWnd = WIN_GetFullHandle( hWnd );
476 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
478 if (!hPopupMenu)
480 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
481 hPopupMenu = MENU_CopySysPopup(TRUE);
482 else
483 hPopupMenu = MENU_CopySysPopup(FALSE);
486 if (hPopupMenu)
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
499 return hMenu;
501 DestroyMenu( hMenu );
503 ERR("failed to load system menu!\n");
504 return 0;
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
515 BOOL gray;
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
529 /* The menu item must keep its state if it's disabled */
530 if(gray)
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
538 * HMENU hMenu )
540 *****************************************************************************/
542 static UINT MENU_GetStartOfNextColumn(
543 HMENU hMenu )
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 UINT i;
548 if(!menu)
549 return NO_SELECTED_ITEM;
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
553 return i;
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return i;
560 return NO_SELECTED_ITEM;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
567 * HMENU hMenu )
569 *****************************************************************************/
571 static UINT MENU_GetStartOfPrevColumn(
572 HMENU hMenu )
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 UINT i;
577 if( !menu )
578 return NO_SELECTED_ITEM;
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
583 /* Find the start of the column */
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 --i); /* empty */
589 if(i == 0)
590 return NO_SELECTED_ITEM;
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 break;
597 TRACE("ret %d.\n", i );
599 return i;
604 /***********************************************************************
605 * MENU_FindItem
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
612 POPUPMENU *menu;
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
615 UINT i;
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
623 else
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
628 if (item->fType & MF_POPUP)
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
632 if (subitem)
634 *hmenu = hsubmenu;
635 return subitem;
637 else if (item->wID == *nPos)
639 /* fallback to this item if nothing else found */
640 fallback_pos = i;
641 fallback = item;
644 else if (item->wID == *nPos)
646 *nPos = i;
647 return item;
652 if (fallback)
653 *nPos = fallback_pos;
655 return fallback;
658 /***********************************************************************
659 * MENU_FindSubMenu
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
667 POPUPMENU *menu;
668 UINT i;
669 MENUITEM *item;
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
673 item = menu->items;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
677 return i;
679 else {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
683 *hmenu = hsubmenu;
684 return pos;
688 return NO_SELECTED_ITEM;
691 /***********************************************************************
692 * MENU_FreeItemData
694 static void MENU_FreeItemData( MENUITEM* item )
696 /* delete text */
697 HeapFree( GetProcessHeap(), 0, item->text );
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
705 static void
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
708 if (menu->bScrolling)
710 UINT arrow_bitmap_height;
711 BITMAP bmp;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
731 MENUITEM *item;
732 UINT i;
733 RECT rect;
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
737 else pt.x -= rect.left;
738 pt.y -= rect.top;
739 item = menu->items;
740 for (i = 0; i < menu->nItems; i++, item++)
742 rect = item->rect;
743 MENU_AdjustMenuItemRect(menu, &rect);
744 if (PtInRect(&rect, pt))
746 if (pos) *pos = i;
747 return item;
750 return NULL;
754 /***********************************************************************
755 * MENU_FindItemByKey
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
760 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
761 WCHAR key, BOOL forceMenuChar )
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
765 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
767 if (hmenu)
769 POPUPMENU *menu = MENU_GetMenu( hmenu );
770 MENUITEM *item = menu->items;
771 LRESULT menuchar;
773 if( !forceMenuChar )
775 UINT i;
776 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
778 for (i = 0; i < menu->nItems; i++, item++)
780 if( item->text)
782 const WCHAR *p = item->text - 2;
785 const WCHAR *q = p + 2;
786 p = strchrW (q, '&');
787 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
789 while (p != NULL && p [1] == '&');
790 if (p && (toupperW(p[1]) == toupperW(key))) return i;
794 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
795 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
796 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
797 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
799 return (UINT)(-1);
803 /***********************************************************************
804 * MENU_GetBitmapItemSize
806 * Get the size of a bitmap item.
808 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
809 HWND hwndOwner)
811 BITMAP bm;
812 HBITMAP bmp = lpitem->hbmpItem;
814 size->cx = size->cy = 0;
816 /* check if there is a magic menu item associated with this item */
817 switch( (INT_PTR) bmp )
819 case (INT_PTR)HBMMENU_CALLBACK:
821 MEASUREITEMSTRUCT measItem;
822 measItem.CtlType = ODT_MENU;
823 measItem.CtlID = 0;
824 measItem.itemID = lpitem->wID;
825 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
826 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
827 measItem.itemData = lpitem->dwItemData;
828 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
829 size->cx = measItem.itemWidth;
830 size->cy = measItem.itemHeight;
831 return;
833 break;
834 case (INT_PTR)HBMMENU_SYSTEM:
835 if (lpitem->dwItemData)
837 bmp = (HBITMAP)lpitem->dwItemData;
838 break;
840 /* fall through */
841 case (INT_PTR)HBMMENU_MBAR_RESTORE:
842 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
843 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
844 case (INT_PTR)HBMMENU_MBAR_CLOSE:
845 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
846 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
847 size->cy = size->cx;
848 return;
849 case (INT_PTR)HBMMENU_POPUP_CLOSE:
850 case (INT_PTR)HBMMENU_POPUP_RESTORE:
851 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
852 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
853 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
854 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
855 return;
857 if (GetObjectW(bmp, sizeof(bm), &bm ))
859 size->cx = bm.bmWidth;
860 size->cy = bm.bmHeight;
864 /***********************************************************************
865 * MENU_DrawBitmapItem
867 * Draw a bitmap item.
869 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
870 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
872 BITMAP bm;
873 DWORD rop;
874 HDC hdcMem;
875 HBITMAP bmp;
876 int w = rect->right - rect->left;
877 int h = rect->bottom - rect->top;
878 int bmp_xoffset = 0;
879 int left, top;
880 HBITMAP hbmToDraw = lpitem->hbmpItem;
881 bmp = hbmToDraw;
883 /* Check if there is a magic menu item associated with this item */
884 if (IS_MAGIC_BITMAP(hbmToDraw))
886 UINT flags = 0;
887 WCHAR bmchr = 0;
888 RECT r;
890 switch((INT_PTR)hbmToDraw)
892 case (INT_PTR)HBMMENU_SYSTEM:
893 if (lpitem->dwItemData)
895 bmp = (HBITMAP)lpitem->dwItemData;
896 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 else
900 static HBITMAP hBmpSysMenu;
902 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
903 bmp = hBmpSysMenu;
904 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
905 /* only use right half of the bitmap */
906 bmp_xoffset = bm.bmWidth / 2;
907 bm.bmWidth -= bmp_xoffset;
909 goto got_bitmap;
910 case (INT_PTR)HBMMENU_MBAR_RESTORE:
911 flags = DFCS_CAPTIONRESTORE;
912 break;
913 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
914 flags = DFCS_CAPTIONMIN;
915 break;
916 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
917 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
918 break;
919 case (INT_PTR)HBMMENU_MBAR_CLOSE:
920 flags = DFCS_CAPTIONCLOSE;
921 break;
922 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
923 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
924 break;
925 case (INT_PTR)HBMMENU_CALLBACK:
927 DRAWITEMSTRUCT drawItem;
928 drawItem.CtlType = ODT_MENU;
929 drawItem.CtlID = 0;
930 drawItem.itemID = lpitem->wID;
931 drawItem.itemAction = odaction;
932 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
933 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
934 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
935 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
936 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
937 drawItem.hwndItem = (HWND)hmenu;
938 drawItem.hDC = hdc;
939 drawItem.itemData = lpitem->dwItemData;
940 drawItem.rcItem = *rect;
941 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
942 return;
944 break;
945 case (INT_PTR)HBMMENU_POPUP_CLOSE:
946 bmchr = 0x72;
947 break;
948 case (INT_PTR)HBMMENU_POPUP_RESTORE:
949 bmchr = 0x32;
950 break;
951 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
952 bmchr = 0x31;
953 break;
954 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
955 bmchr = 0x30;
956 break;
957 default:
958 FIXME("Magic %p not implemented\n", hbmToDraw);
959 return;
961 if (bmchr)
963 /* draw the magic bitmaps using marlett font characters */
964 /* FIXME: fontsize and the position (x,y) could probably be better */
965 HFONT hfont, hfontsav;
966 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
967 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
968 { 'M','a','r','l','e','t','t',0 } };
969 logfont.lfHeight = min( h, w) - 5 ;
970 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
971 hfont = CreateFontIndirectW( &logfont);
972 hfontsav = SelectObject(hdc, hfont);
973 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
974 SelectObject(hdc, hfontsav);
975 DeleteObject( hfont);
977 else
979 r = *rect;
980 InflateRect( &r, -1, -1 );
981 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
982 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
984 return;
987 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
989 got_bitmap:
990 hdcMem = CreateCompatibleDC( hdc );
991 SelectObject( hdcMem, bmp );
993 /* handle fontsize > bitmap_height */
994 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
995 left=rect->left;
996 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
997 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
998 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
999 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1000 DeleteDC( hdcMem );
1004 /***********************************************************************
1005 * MENU_CalcItemSize
1007 * Calculate the size of the menu item and store it in lpitem->rect.
1009 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1010 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1012 WCHAR *p;
1013 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1014 UINT arrow_bitmap_width;
1015 BITMAP bm;
1016 INT itemheight;
1018 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1019 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1020 (menuBar ? " (MenuBar)" : ""));
1022 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1023 arrow_bitmap_width = bm.bmWidth;
1025 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1026 if( !menucharsize.cx ) {
1027 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1028 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1029 * but it is unlikely an application will depend on that */
1030 ODitemheight = HIWORD( GetDialogBaseUnits());
1033 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1035 if (lpitem->fType & MF_OWNERDRAW)
1037 MEASUREITEMSTRUCT mis;
1038 mis.CtlType = ODT_MENU;
1039 mis.CtlID = 0;
1040 mis.itemID = lpitem->wID;
1041 mis.itemData = lpitem->dwItemData;
1042 mis.itemHeight = ODitemheight;
1043 mis.itemWidth = 0;
1044 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1045 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1046 * width of a menufont character to the width of an owner-drawn menu.
1048 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1049 if (menuBar) {
1050 /* under at least win95 you seem to be given a standard
1051 height for the menu and the height value is ignored */
1052 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1053 } else
1054 lpitem->rect.bottom += mis.itemHeight;
1056 TRACE("id=%04lx size=%dx%d\n",
1057 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1058 lpitem->rect.bottom-lpitem->rect.top);
1059 return;
1062 if (lpitem->fType & MF_SEPARATOR)
1064 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1065 if( !menuBar)
1066 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1067 return;
1070 itemheight = 0;
1071 lpitem->xTab = 0;
1073 if (!menuBar) {
1074 if (lpitem->hbmpItem) {
1075 SIZE size;
1077 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1078 /* Keep the size of the bitmap in callback mode to be able
1079 * to draw it correctly */
1080 lpitem->bmpsize = size;
1081 lppop->textOffset = max( lppop->textOffset, size.cx);
1082 lpitem->rect.right += size.cx + 2;
1083 itemheight = size.cy + 2;
1085 if( !(lppop->dwStyle & MNS_NOCHECK))
1086 lpitem->rect.right += check_bitmap_width;
1087 lpitem->rect.right += 4 + menucharsize.cx;
1088 lpitem->xTab = lpitem->rect.right;
1089 lpitem->rect.right += arrow_bitmap_width;
1090 } else if (lpitem->hbmpItem) { /* menuBar */
1091 SIZE size;
1093 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1094 lpitem->bmpsize = size;
1095 lpitem->rect.right += size.cx;
1096 if( lpitem->text) lpitem->rect.right += 2;
1097 itemheight = size.cy;
1100 /* it must be a text item - unless it's the system menu */
1101 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1102 HFONT hfontOld = NULL;
1103 RECT rc = lpitem->rect;
1104 LONG txtheight, txtwidth;
1106 if ( lpitem->fState & MFS_DEFAULT ) {
1107 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1109 if (menuBar) {
1110 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1111 DT_SINGLELINE|DT_CALCRECT);
1112 lpitem->rect.right += rc.right - rc.left;
1113 itemheight = max( max( itemheight, txtheight),
1114 GetSystemMetrics( SM_CYMENU) - 1);
1115 lpitem->rect.right += 2 * menucharsize.cx;
1116 } else {
1117 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1118 RECT tmprc = rc;
1119 LONG tmpheight;
1120 int n = (int)( p - lpitem->text);
1121 /* Item contains a tab (only meaningful in popup menus) */
1122 /* get text size before the tab */
1123 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1124 DT_SINGLELINE|DT_CALCRECT);
1125 txtwidth = rc.right - rc.left;
1126 p += 1; /* advance past the Tab */
1127 /* get text size after the tab */
1128 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 lpitem->xTab += txtwidth;
1131 txtheight = max( txtheight, tmpheight);
1132 txtwidth += menucharsize.cx + /* space for the tab */
1133 tmprc.right - tmprc.left; /* space for the short cut */
1134 } else {
1135 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1136 DT_SINGLELINE|DT_CALCRECT);
1137 txtwidth = rc.right - rc.left;
1138 lpitem->xTab += txtwidth;
1140 lpitem->rect.right += 2 + txtwidth;
1141 itemheight = max( itemheight,
1142 max( txtheight + 2, menucharsize.cy + 4));
1144 if (hfontOld) SelectObject (hdc, hfontOld);
1145 } else if( menuBar) {
1146 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1148 lpitem->rect.bottom += itemheight;
1149 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1153 /***********************************************************************
1154 * MENU_GetMaxPopupHeight
1156 static UINT
1157 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1159 if (lppop->cyMax)
1160 return lppop->cyMax;
1161 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1165 /***********************************************************************
1166 * MENU_PopupMenuCalcSize
1168 * Calculate the size of a popup menu.
1170 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1172 MENUITEM *lpitem;
1173 HDC hdc;
1174 UINT start, i;
1175 BOOL textandbmp = FALSE;
1176 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1178 lppop->Width = lppop->Height = 0;
1179 if (lppop->nItems == 0) return;
1180 hdc = GetDC( 0 );
1182 SelectObject( hdc, get_menu_font(FALSE));
1184 start = 0;
1185 maxX = 2 + 1;
1187 lppop->textOffset = 0;
1189 while (start < lppop->nItems)
1191 lpitem = &lppop->items[start];
1192 orgX = maxX;
1193 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1194 orgX += MENU_COL_SPACE;
1195 orgY = MENU_TOP_MARGIN;
1197 maxTab = maxTabWidth = 0;
1198 /* Parse items until column break or end of menu */
1199 for (i = start; i < lppop->nItems; i++, lpitem++)
1201 if ((i != start) &&
1202 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1204 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1205 maxX = max( maxX, lpitem->rect.right );
1206 orgY = lpitem->rect.bottom;
1207 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1209 maxTab = max( maxTab, lpitem->xTab );
1210 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1212 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1215 /* Finish the column (set all items to the largest width found) */
1216 maxX = max( maxX, maxTab + maxTabWidth );
1217 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1219 lpitem->rect.right = maxX;
1220 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1221 lpitem->xTab = maxTab;
1224 lppop->Height = max( lppop->Height, orgY );
1227 lppop->Width = maxX;
1228 /* if none of the items have both text and bitmap then
1229 * the text and bitmaps are all aligned on the left. If there is at
1230 * least one item with both text and bitmap then bitmaps are
1231 * on the left and texts left aligned with the right hand side
1232 * of the bitmaps */
1233 if( !textandbmp) lppop->textOffset = 0;
1235 /* space for 3d border */
1236 lppop->Height += MENU_BOTTOM_MARGIN;
1237 lppop->Width += 2;
1239 /* Adjust popup height if it exceeds maximum */
1240 maxHeight = MENU_GetMaxPopupHeight(lppop);
1241 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1242 if (lppop->Height >= maxHeight)
1244 lppop->Height = maxHeight;
1245 lppop->bScrolling = TRUE;
1247 else
1249 lppop->bScrolling = FALSE;
1252 ReleaseDC( 0, hdc );
1256 /***********************************************************************
1257 * MENU_MenuBarCalcSize
1259 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1260 * height is off by 1 pixel which causes lengthy window relocations when
1261 * active document window is maximized/restored.
1263 * Calculate the size of the menu bar.
1265 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1266 LPPOPUPMENU lppop, HWND hwndOwner )
1268 MENUITEM *lpitem;
1269 UINT start, i, helpPos;
1270 int orgX, orgY, maxY;
1272 if ((lprect == NULL) || (lppop == NULL)) return;
1273 if (lppop->nItems == 0) return;
1274 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1275 lppop->Width = lprect->right - lprect->left;
1276 lppop->Height = 0;
1277 maxY = lprect->top+1;
1278 start = 0;
1279 helpPos = ~0U;
1280 lppop->textOffset = 0;
1281 while (start < lppop->nItems)
1283 lpitem = &lppop->items[start];
1284 orgX = lprect->left;
1285 orgY = maxY;
1287 /* Parse items until line break or end of menu */
1288 for (i = start; i < lppop->nItems; i++, lpitem++)
1290 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1291 if ((i != start) &&
1292 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1294 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1295 debug_print_menuitem (" item: ", lpitem, "");
1296 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1298 if (lpitem->rect.right > lprect->right)
1300 if (i != start) break;
1301 else lpitem->rect.right = lprect->right;
1303 maxY = max( maxY, lpitem->rect.bottom );
1304 orgX = lpitem->rect.right;
1307 /* Finish the line (set all items to the largest height found) */
1308 while (start < i) lppop->items[start++].rect.bottom = maxY;
1311 lprect->bottom = maxY;
1312 lppop->Height = lprect->bottom - lprect->top;
1314 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1315 /* the last item (if several lines, only move the last line) */
1316 if (helpPos == ~0U) return;
1317 lpitem = &lppop->items[lppop->nItems-1];
1318 orgY = lpitem->rect.top;
1319 orgX = lprect->right;
1320 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1321 if (lpitem->rect.top != orgY) break; /* Other line */
1322 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1323 lpitem->rect.left += orgX - lpitem->rect.right;
1324 lpitem->rect.right = orgX;
1325 orgX = lpitem->rect.left;
1330 /***********************************************************************
1331 * MENU_DrawScrollArrows
1333 * Draw scroll arrows.
1335 static void
1336 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1338 HDC hdcMem = CreateCompatibleDC(hdc);
1339 HBITMAP hOrigBitmap;
1340 UINT arrow_bitmap_width, arrow_bitmap_height;
1341 BITMAP bmp;
1342 RECT rect;
1344 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1345 arrow_bitmap_width = bmp.bmWidth;
1346 arrow_bitmap_height = bmp.bmHeight;
1349 if (lppop->nScrollPos)
1350 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1351 else
1352 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1353 rect.left = 0;
1354 rect.top = 0;
1355 rect.right = lppop->Width;
1356 rect.bottom = arrow_bitmap_height;
1357 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1358 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1359 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1360 rect.top = lppop->Height - arrow_bitmap_height;
1361 rect.bottom = lppop->Height;
1362 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1363 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1364 SelectObject(hdcMem, get_down_arrow_bitmap());
1365 else
1366 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1367 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1368 lppop->Height - arrow_bitmap_height,
1369 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1370 SelectObject(hdcMem, hOrigBitmap);
1371 DeleteDC(hdcMem);
1375 /***********************************************************************
1376 * draw_popup_arrow
1378 * Draws the popup-menu arrow.
1380 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1381 UINT arrow_bitmap_height)
1383 HDC hdcMem = CreateCompatibleDC( hdc );
1384 HBITMAP hOrigBitmap;
1386 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1387 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1388 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1389 arrow_bitmap_width, arrow_bitmap_height,
1390 hdcMem, 0, 0, SRCCOPY );
1391 SelectObject( hdcMem, hOrigBitmap );
1392 DeleteDC( hdcMem );
1394 /***********************************************************************
1395 * MENU_DrawMenuItem
1397 * Draw a single menu item.
1399 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1400 UINT height, BOOL menuBar, UINT odaction )
1402 RECT rect;
1403 BOOL flat_menu = FALSE;
1404 int bkgnd;
1405 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1406 POPUPMENU *menu = MENU_GetMenu(hmenu);
1407 RECT bmprc;
1409 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1411 if (!menuBar) {
1412 BITMAP bmp;
1413 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1414 arrow_bitmap_width = bmp.bmWidth;
1415 arrow_bitmap_height = bmp.bmHeight;
1418 if (lpitem->fType & MF_SYSMENU)
1420 if( !IsIconic(hwnd) )
1421 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1422 return;
1425 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1426 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1428 /* Setup colors */
1430 if (lpitem->fState & MF_HILITE)
1432 if(menuBar && !flat_menu) {
1433 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1434 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1435 } else {
1436 if(lpitem->fState & MF_GRAYED)
1437 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1438 else
1439 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1440 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1443 else
1445 if (lpitem->fState & MF_GRAYED)
1446 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1447 else
1448 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1449 SetBkColor( hdc, GetSysColor( bkgnd ) );
1452 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1453 rect = lpitem->rect;
1454 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1456 if (lpitem->fType & MF_OWNERDRAW)
1459 ** Experimentation under Windows reveals that an owner-drawn
1460 ** menu is given the rectangle which includes the space it requested
1461 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1462 ** and a popup-menu arrow. This is the value of lpitem->rect.
1463 ** Windows will leave all drawing to the application except for
1464 ** the popup-menu arrow. Windows always draws that itself, after
1465 ** the menu owner has finished drawing.
1467 DRAWITEMSTRUCT dis;
1469 dis.CtlType = ODT_MENU;
1470 dis.CtlID = 0;
1471 dis.itemID = lpitem->wID;
1472 dis.itemData = lpitem->dwItemData;
1473 dis.itemState = 0;
1474 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1475 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1476 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1477 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1478 dis.hwndItem = (HWND)hmenu;
1479 dis.hDC = hdc;
1480 dis.rcItem = rect;
1481 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1482 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1483 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1484 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1485 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1486 /* Draw the popup-menu arrow */
1487 if (lpitem->fType & MF_POPUP)
1488 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1489 arrow_bitmap_height);
1490 return;
1493 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1495 if (lpitem->fState & MF_HILITE)
1497 if (flat_menu)
1499 InflateRect (&rect, -1, -1);
1500 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1501 InflateRect (&rect, 1, 1);
1502 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1504 else
1506 if(menuBar)
1507 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1508 else
1509 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1512 else
1513 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1515 SetBkMode( hdc, TRANSPARENT );
1517 /* vertical separator */
1518 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1520 HPEN oldPen;
1521 RECT rc = rect;
1523 rc.left -= MENU_COL_SPACE / 2 + 1;
1524 rc.top = 3;
1525 rc.bottom = height - 3;
1526 if (flat_menu)
1528 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1529 MoveToEx( hdc, rc.left, rc.top, NULL );
1530 LineTo( hdc, rc.left, rc.bottom );
1531 SelectObject( hdc, oldPen );
1533 else
1534 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1537 /* horizontal separator */
1538 if (lpitem->fType & MF_SEPARATOR)
1540 HPEN oldPen;
1541 RECT rc = rect;
1543 rc.left++;
1544 rc.right--;
1545 rc.top = ( rc.top + rc.bottom) / 2;
1546 if (flat_menu)
1548 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1549 MoveToEx( hdc, rc.left, rc.top, NULL );
1550 LineTo( hdc, rc.right, rc.top );
1551 SelectObject( hdc, oldPen );
1553 else
1554 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1555 return;
1558 /* helper lines for debugging */
1559 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1560 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1561 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1562 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1565 if (lpitem->hbmpItem) {
1566 /* calculate the bitmap rectangle in coordinates relative
1567 * to the item rectangle */
1568 if( menuBar) {
1569 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1570 bmprc.left = 3;
1571 else
1572 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1574 else if (menu->dwStyle & MNS_NOCHECK)
1575 bmprc.left = 4;
1576 else if (menu->dwStyle & MNS_CHECKORBMP)
1577 bmprc.left = 2;
1578 else
1579 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1580 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1581 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1582 bmprc.top = 0;
1583 else
1584 bmprc.top = (rect.bottom - rect.top -
1585 lpitem->bmpsize.cy) / 2;
1586 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1589 if (!menuBar)
1591 HBITMAP bm;
1592 INT y = rect.top + rect.bottom;
1593 RECT rc = rect;
1594 BOOL checked = FALSE;
1595 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1596 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1597 /* Draw the check mark
1599 * FIXME:
1600 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1602 if( !(menu->dwStyle & MNS_NOCHECK)) {
1603 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1604 lpitem->hUnCheckBit;
1605 if (bm) /* we have a custom bitmap */
1607 HDC hdcMem = CreateCompatibleDC( hdc );
1609 SelectObject( hdcMem, bm );
1610 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1611 check_bitmap_width, check_bitmap_height,
1612 hdcMem, 0, 0, SRCCOPY );
1613 DeleteDC( hdcMem );
1614 checked = TRUE;
1616 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1618 RECT r;
1619 HBITMAP bm = CreateBitmap( check_bitmap_width,
1620 check_bitmap_height, 1, 1, NULL );
1621 HDC hdcMem = CreateCompatibleDC( hdc );
1623 SelectObject( hdcMem, bm );
1624 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1625 DrawFrameControl( hdcMem, &r, DFC_MENU,
1626 (lpitem->fType & MFT_RADIOCHECK) ?
1627 DFCS_MENUBULLET : DFCS_MENUCHECK );
1628 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1629 hdcMem, 0, 0, SRCCOPY );
1630 DeleteDC( hdcMem );
1631 DeleteObject( bm );
1632 checked = TRUE;
1635 if( lpitem->hbmpItem &&
1636 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1637 POINT origorg;
1638 /* some applications make this assumption on the DC's origin */
1639 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1640 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1641 odaction, FALSE);
1642 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1644 /* Draw the popup-menu arrow */
1645 if (lpitem->fType & MF_POPUP)
1646 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1647 arrow_bitmap_height);
1648 rect.left += 4;
1649 if( !(menu->dwStyle & MNS_NOCHECK))
1650 rect.left += check_bitmap_width;
1651 rect.right -= arrow_bitmap_width;
1653 else if( lpitem->hbmpItem)
1654 { /* Draw the bitmap */
1655 POINT origorg;
1657 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1658 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1659 odaction, menuBar);
1660 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1662 /* process text if present */
1663 if (lpitem->text)
1665 int i;
1666 HFONT hfontOld = 0;
1668 UINT uFormat = (menuBar) ?
1669 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1670 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1672 if( !(menu->dwStyle & MNS_CHECKORBMP))
1673 rect.left += menu->textOffset;
1675 if ( lpitem->fState & MFS_DEFAULT )
1677 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1680 if (menuBar) {
1681 if( lpitem->hbmpItem)
1682 rect.left += lpitem->bmpsize.cx;
1683 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1684 rect.left += menucharsize.cx;
1685 rect.right -= menucharsize.cx;
1688 for (i = 0; lpitem->text[i]; i++)
1689 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1690 break;
1692 if(lpitem->fState & MF_GRAYED)
1694 if (!(lpitem->fState & MF_HILITE) )
1696 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1697 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1698 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1699 --rect.left; --rect.top; --rect.right; --rect.bottom;
1701 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1704 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1706 /* paint the shortcut text */
1707 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1709 if (lpitem->text[i] == '\t')
1711 rect.left = lpitem->xTab;
1712 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1714 else
1716 rect.right = lpitem->xTab;
1717 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1720 if(lpitem->fState & MF_GRAYED)
1722 if (!(lpitem->fState & MF_HILITE) )
1724 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1725 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1726 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1727 --rect.left; --rect.top; --rect.right; --rect.bottom;
1729 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1731 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1734 if (hfontOld)
1735 SelectObject (hdc, hfontOld);
1740 /***********************************************************************
1741 * MENU_DrawPopupMenu
1743 * Paint a popup menu.
1745 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1747 HBRUSH hPrevBrush = 0;
1748 RECT rect;
1750 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1752 GetClientRect( hwnd, &rect );
1754 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1755 && (SelectObject( hdc, get_menu_font(FALSE))))
1757 HPEN hPrevPen;
1759 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1761 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1762 if( hPrevPen )
1764 POPUPMENU *menu;
1765 BOOL flat_menu = FALSE;
1767 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1768 if (flat_menu)
1769 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1770 else
1771 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1773 if( (menu = MENU_GetMenu( hmenu )))
1775 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1776 /* draw menu items */
1777 if( menu->nItems)
1779 MENUITEM *item;
1780 UINT u;
1782 item = menu->items;
1783 for( u = menu->nItems; u > 0; u--, item++)
1784 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1785 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1787 /* draw scroll arrows */
1788 if (menu->bScrolling)
1789 MENU_DrawScrollArrows(menu, hdc);
1791 } else
1793 SelectObject( hdc, hPrevBrush );
1798 /***********************************************************************
1799 * MENU_DrawMenuBar
1801 * Paint a menu bar. Returns the height of the menu bar.
1802 * called from [windows/nonclient.c]
1804 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1806 LPPOPUPMENU lppop;
1807 HMENU hMenu = GetMenu(hwnd);
1809 lppop = MENU_GetMenu( hMenu );
1810 if (lppop == NULL || lprect == NULL)
1812 return GetSystemMetrics(SM_CYMENU);
1815 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1819 /***********************************************************************
1820 * MENU_ShowPopup
1822 * Display a popup menu.
1824 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1825 INT x, INT y, INT xanchor, INT yanchor )
1827 POPUPMENU *menu;
1828 INT width, height;
1829 POINT pt;
1830 HMONITOR monitor;
1831 MONITORINFO info;
1832 DWORD ex_style = 0;
1834 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1835 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1837 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1838 if (menu->FocusedItem != NO_SELECTED_ITEM)
1840 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1841 menu->FocusedItem = NO_SELECTED_ITEM;
1844 /* store the owner for DrawItem */
1845 if (!IsWindow( hwndOwner ))
1847 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1848 return FALSE;
1850 menu->hwndOwner = hwndOwner;
1852 menu->nScrollPos = 0;
1853 MENU_PopupMenuCalcSize( menu );
1855 /* adjust popup menu pos so that it fits within the desktop */
1857 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1858 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1860 /* FIXME: should use item rect */
1861 pt.x = x;
1862 pt.y = y;
1863 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1864 info.cbSize = sizeof(info);
1865 GetMonitorInfoW( monitor, &info );
1867 if (flags & TPM_LAYOUTRTL)
1869 ex_style = WS_EX_LAYOUTRTL;
1870 flags ^= TPM_RIGHTALIGN;
1873 if( flags & TPM_RIGHTALIGN ) x -= width;
1874 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1876 if( flags & TPM_BOTTOMALIGN ) y -= height;
1877 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1879 if( x + width > info.rcWork.right)
1881 if( xanchor && x >= width - xanchor )
1882 x -= width - xanchor;
1884 if( x + width > info.rcWork.right)
1885 x = info.rcWork.right - width;
1887 if( x < info.rcWork.left ) x = info.rcWork.left;
1889 if( y + height > info.rcWork.bottom)
1891 if( yanchor && y >= height + yanchor )
1892 y -= height + yanchor;
1894 if( y + height > info.rcWork.bottom)
1895 y = info.rcWork.bottom - height;
1897 if( y < info.rcWork.top ) y = info.rcWork.top;
1899 /* NOTE: In Windows, top menu popup is not owned. */
1900 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1901 WS_POPUP, x, y, width, height,
1902 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1903 (LPVOID)hmenu );
1904 if( !menu->hWnd ) return FALSE;
1905 if (!top_popup) {
1906 top_popup = menu->hWnd;
1907 top_popup_hmenu = hmenu;
1909 /* Display the window */
1911 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1912 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1913 UpdateWindow( menu->hWnd );
1914 return TRUE;
1918 /***********************************************************************
1919 * MENU_EnsureMenuItemVisible
1921 static void
1922 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1924 if (lppop->bScrolling)
1926 MENUITEM *item = &lppop->items[wIndex];
1927 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1928 UINT nOldPos = lppop->nScrollPos;
1929 RECT rc;
1930 UINT arrow_bitmap_height;
1931 BITMAP bmp;
1933 GetClientRect(lppop->hWnd, &rc);
1935 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1936 arrow_bitmap_height = bmp.bmHeight;
1938 rc.top += arrow_bitmap_height;
1939 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1941 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1942 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1945 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1946 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1947 MENU_DrawScrollArrows(lppop, hdc);
1949 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1951 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1952 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1953 MENU_DrawScrollArrows(lppop, hdc);
1959 /***********************************************************************
1960 * MENU_SelectItem
1962 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1963 BOOL sendMenuSelect, HMENU topmenu )
1965 LPPOPUPMENU lppop;
1966 HDC hdc;
1968 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1970 lppop = MENU_GetMenu( hmenu );
1971 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1973 if (lppop->FocusedItem == wIndex) return;
1974 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1975 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1976 if (!top_popup) {
1977 top_popup = lppop->hWnd;
1978 top_popup_hmenu = hmenu;
1981 SelectObject( hdc, get_menu_font(FALSE));
1983 /* Clear previous highlighted item */
1984 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1986 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1987 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1988 lppop->Height, !(lppop->wFlags & MF_POPUP),
1989 ODA_SELECT );
1992 /* Highlight new item (if any) */
1993 lppop->FocusedItem = wIndex;
1994 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1996 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1997 lppop->items[wIndex].fState |= MF_HILITE;
1998 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1999 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2000 &lppop->items[wIndex], lppop->Height,
2001 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2003 if (sendMenuSelect)
2005 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2006 SendMessageW( hwndOwner, WM_MENUSELECT,
2007 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2008 ip->fType | ip->fState |
2009 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2012 else if (sendMenuSelect) {
2013 if(topmenu){
2014 int pos;
2015 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2016 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2017 MENUITEM *ip = &ptm->items[pos];
2018 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2019 ip->fType | ip->fState |
2020 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2024 ReleaseDC( lppop->hWnd, hdc );
2028 /***********************************************************************
2029 * MENU_MoveSelection
2031 * Moves currently selected item according to the offset parameter.
2032 * If there is no selection then it should select the last item if
2033 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2035 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2037 INT i;
2038 POPUPMENU *menu;
2040 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2042 menu = MENU_GetMenu( hmenu );
2043 if ((!menu) || (!menu->items)) return;
2045 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2047 if( menu->nItems == 1 ) return; else
2048 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2049 ; i += offset)
2050 if (!(menu->items[i].fType & MF_SEPARATOR))
2052 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2053 return;
2057 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2058 i >= 0 && i < menu->nItems ; i += offset)
2059 if (!(menu->items[i].fType & MF_SEPARATOR))
2061 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2062 return;
2067 /**********************************************************************
2068 * MENU_InsertItem
2070 * Insert (allocate) a new item into a menu.
2072 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2074 MENUITEM *newItems;
2075 POPUPMENU *menu;
2077 if (!(menu = MENU_GetMenu(hMenu)))
2078 return NULL;
2080 /* Find where to insert new item */
2082 if (flags & MF_BYPOSITION) {
2083 if (pos > menu->nItems)
2084 pos = menu->nItems;
2085 } else {
2086 if (!MENU_FindItem( &hMenu, &pos, flags ))
2087 pos = menu->nItems;
2088 else {
2089 if (!(menu = MENU_GetMenu( hMenu )))
2090 return NULL;
2094 /* Make sure that MDI system buttons stay on the right side.
2095 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2096 * regardless of their id.
2098 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2099 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2100 pos--;
2102 TRACE("inserting at %u flags %x\n", pos, flags);
2104 /* Create new items array */
2106 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2107 if (!newItems)
2109 WARN("allocation failed\n" );
2110 return NULL;
2112 if (menu->nItems > 0)
2114 /* Copy the old array into the new one */
2115 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2116 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2117 (menu->nItems-pos)*sizeof(MENUITEM) );
2118 HeapFree( GetProcessHeap(), 0, menu->items );
2120 menu->items = newItems;
2121 menu->nItems++;
2122 memset( &newItems[pos], 0, sizeof(*newItems) );
2123 menu->Height = 0; /* force size recalculate */
2124 return &newItems[pos];
2128 /**********************************************************************
2129 * MENU_ParseResource
2131 * Parse a standard menu resource and add items to the menu.
2132 * Return a pointer to the end of the resource.
2134 * NOTE: flags is equivalent to the mtOption field
2136 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2138 WORD flags, id = 0;
2139 LPCWSTR str;
2140 BOOL end_flag;
2144 flags = GET_WORD(res);
2145 end_flag = flags & MF_END;
2146 /* Remove MF_END because it has the same value as MF_HILITE */
2147 flags &= ~MF_END;
2148 res += sizeof(WORD);
2149 if (!(flags & MF_POPUP))
2151 id = GET_WORD(res);
2152 res += sizeof(WORD);
2154 str = (LPCWSTR)res;
2155 res += (strlenW(str) + 1) * sizeof(WCHAR);
2156 if (flags & MF_POPUP)
2158 HMENU hSubMenu = CreatePopupMenu();
2159 if (!hSubMenu) return NULL;
2160 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2161 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2163 else /* Not a popup */
2165 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2167 } while (!end_flag);
2168 return res;
2172 /**********************************************************************
2173 * MENUEX_ParseResource
2175 * Parse an extended menu resource and add items to the menu.
2176 * Return a pointer to the end of the resource.
2178 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2180 WORD resinfo;
2181 do {
2182 MENUITEMINFOW mii;
2184 mii.cbSize = sizeof(mii);
2185 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2186 mii.fType = GET_DWORD(res);
2187 res += sizeof(DWORD);
2188 mii.fState = GET_DWORD(res);
2189 res += sizeof(DWORD);
2190 mii.wID = GET_DWORD(res);
2191 res += sizeof(DWORD);
2192 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2193 res += sizeof(WORD);
2194 /* Align the text on a word boundary. */
2195 res += (~((UINT_PTR)res - 1)) & 1;
2196 mii.dwTypeData = (LPWSTR) res;
2197 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2198 /* Align the following fields on a dword boundary. */
2199 res += (~((UINT_PTR)res - 1)) & 3;
2201 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2202 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2204 if (resinfo & 1) { /* Pop-up? */
2205 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2206 res += sizeof(DWORD);
2207 mii.hSubMenu = CreatePopupMenu();
2208 if (!mii.hSubMenu)
2209 return NULL;
2210 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2211 DestroyMenu(mii.hSubMenu);
2212 return NULL;
2214 mii.fMask |= MIIM_SUBMENU;
2215 mii.fType |= MF_POPUP;
2217 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2219 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2220 mii.wID, mii.fType);
2221 mii.fType |= MF_SEPARATOR;
2223 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2224 } while (!(resinfo & MF_END));
2225 return res;
2229 /***********************************************************************
2230 * MENU_GetSubPopup
2232 * Return the handle of the selected sub-popup menu (if any).
2234 static HMENU MENU_GetSubPopup( HMENU hmenu )
2236 POPUPMENU *menu;
2237 MENUITEM *item;
2239 menu = MENU_GetMenu( hmenu );
2241 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2243 item = &menu->items[menu->FocusedItem];
2244 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2245 return item->hSubMenu;
2246 return 0;
2250 /***********************************************************************
2251 * MENU_HideSubPopups
2253 * Hide the sub-popup menus of this menu.
2255 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2256 BOOL sendMenuSelect, UINT wFlags )
2258 POPUPMENU *menu = MENU_GetMenu( hmenu );
2260 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2262 if (menu && top_popup)
2264 HMENU hsubmenu;
2265 POPUPMENU *submenu;
2266 MENUITEM *item;
2268 if (menu->FocusedItem != NO_SELECTED_ITEM)
2270 item = &menu->items[menu->FocusedItem];
2271 if (!(item->fType & MF_POPUP) ||
2272 !(item->fState & MF_MOUSESELECT)) return;
2273 item->fState &= ~MF_MOUSESELECT;
2274 hsubmenu = item->hSubMenu;
2275 } else return;
2277 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2278 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2279 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2280 DestroyWindow( submenu->hWnd );
2281 submenu->hWnd = 0;
2283 if (!(wFlags & TPM_NONOTIFY))
2284 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2285 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2290 /***********************************************************************
2291 * MENU_ShowSubPopup
2293 * Display the sub-menu of the selected item of this menu.
2294 * Return the handle of the submenu, or hmenu if no submenu to display.
2296 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2297 BOOL selectFirst, UINT wFlags )
2299 RECT rect;
2300 POPUPMENU *menu;
2301 MENUITEM *item;
2302 HDC hdc;
2304 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2306 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2308 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2310 item = &menu->items[menu->FocusedItem];
2311 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2312 return hmenu;
2314 /* message must be sent before using item,
2315 because nearly everything may be changed by the application ! */
2317 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2318 if (!(wFlags & TPM_NONOTIFY))
2319 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2320 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2322 item = &menu->items[menu->FocusedItem];
2323 rect = item->rect;
2325 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2326 if (!(item->fState & MF_HILITE))
2328 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2329 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2331 SelectObject( hdc, get_menu_font(FALSE));
2333 item->fState |= MF_HILITE;
2334 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2335 ReleaseDC( menu->hWnd, hdc );
2337 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2338 item->rect = rect;
2340 item->fState |= MF_MOUSESELECT;
2342 if (IS_SYSTEM_MENU(menu))
2344 MENU_InitSysMenuPopup(item->hSubMenu,
2345 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2346 GetClassLongW( menu->hWnd, GCL_STYLE));
2348 NC_GetSysPopupPos( menu->hWnd, &rect );
2349 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2350 rect.top = rect.bottom;
2351 rect.right = GetSystemMetrics(SM_CXSIZE);
2352 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2354 else
2356 GetWindowRect( menu->hWnd, &rect );
2357 if (menu->wFlags & MF_POPUP)
2359 RECT rc = item->rect;
2361 MENU_AdjustMenuItemRect(menu, &rc);
2363 /* The first item in the popup menu has to be at the
2364 same y position as the focused menu item */
2365 if (wFlags & TPM_LAYOUTRTL)
2366 rect.left += GetSystemMetrics(SM_CXBORDER);
2367 else
2368 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2369 rect.top += rc.top - MENU_TOP_MARGIN;
2370 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2371 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2372 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2374 else
2376 if (wFlags & TPM_LAYOUTRTL)
2377 rect.left = rect.right - item->rect.left;
2378 else
2379 rect.left += item->rect.left;
2380 rect.top += item->rect.bottom;
2381 rect.right = item->rect.right - item->rect.left;
2382 rect.bottom = item->rect.bottom - item->rect.top;
2386 /* use default alignment for submenus */
2387 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2389 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2390 rect.left, rect.top, rect.right, rect.bottom );
2391 if (selectFirst)
2392 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2393 return item->hSubMenu;
2398 /**********************************************************************
2399 * MENU_IsMenuActive
2401 HWND MENU_IsMenuActive(void)
2403 return top_popup;
2406 /**********************************************************************
2407 * MENU_EndMenu
2409 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2411 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2413 void MENU_EndMenu( HWND hwnd )
2415 POPUPMENU *menu;
2416 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2417 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2420 /***********************************************************************
2421 * MENU_PtMenu
2423 * Walks menu chain trying to find a menu pt maps to.
2425 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2427 POPUPMENU *menu = MENU_GetMenu( hMenu );
2428 UINT item = menu->FocusedItem;
2429 HMENU ret;
2431 /* try subpopup first (if any) */
2432 ret = (item != NO_SELECTED_ITEM &&
2433 (menu->items[item].fType & MF_POPUP) &&
2434 (menu->items[item].fState & MF_MOUSESELECT))
2435 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2437 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2439 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2440 if( menu->wFlags & MF_POPUP )
2442 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2444 else if (ht == HTSYSMENU)
2445 ret = get_win_sys_menu( menu->hWnd );
2446 else if (ht == HTMENU)
2447 ret = GetMenu( menu->hWnd );
2449 return ret;
2452 /***********************************************************************
2453 * MENU_ExecFocusedItem
2455 * Execute a menu item (for instance when user pressed Enter).
2456 * Return the wID of the executed item. Otherwise, -1 indicating
2457 * that no menu item was executed, -2 if a popup is shown;
2458 * Have to receive the flags for the TrackPopupMenu options to avoid
2459 * sending unwanted message.
2462 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2464 MENUITEM *item;
2465 POPUPMENU *menu = MENU_GetMenu( hMenu );
2467 TRACE("%p hmenu=%p\n", pmt, hMenu);
2469 if (!menu || !menu->nItems ||
2470 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2472 item = &menu->items[menu->FocusedItem];
2474 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2476 if (!(item->fType & MF_POPUP))
2478 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2480 /* If TPM_RETURNCMD is set you return the id, but
2481 do not send a message to the owner */
2482 if(!(wFlags & TPM_RETURNCMD))
2484 if( menu->wFlags & MF_SYSMENU )
2485 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2486 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2487 else
2489 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2490 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2492 if (dwStyle & MNS_NOTIFYBYPOS)
2493 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2494 (LPARAM)hMenu);
2495 else
2496 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2499 return item->wID;
2502 else
2504 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2505 return -2;
2508 return -1;
2511 /***********************************************************************
2512 * MENU_SwitchTracking
2514 * Helper function for menu navigation routines.
2516 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2518 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2519 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2521 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2523 if( pmt->hTopMenu != hPtMenu &&
2524 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2526 /* both are top level menus (system and menu-bar) */
2527 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2528 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2529 pmt->hTopMenu = hPtMenu;
2531 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2532 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2536 /***********************************************************************
2537 * MENU_ButtonDown
2539 * Return TRUE if we can go on with menu tracking.
2541 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2543 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2545 if (hPtMenu)
2547 UINT id = 0;
2548 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2549 MENUITEM *item;
2551 if( IS_SYSTEM_MENU(ptmenu) )
2552 item = ptmenu->items;
2553 else
2554 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2556 if( item )
2558 if( ptmenu->FocusedItem != id )
2559 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2561 /* If the popup menu is not already "popped" */
2562 if(!(item->fState & MF_MOUSESELECT ))
2564 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2567 return TRUE;
2569 /* Else the click was on the menu bar, finish the tracking */
2571 return FALSE;
2574 /***********************************************************************
2575 * MENU_ButtonUp
2577 * Return the value of MENU_ExecFocusedItem if
2578 * the selected item was not a popup. Else open the popup.
2579 * A -1 return value indicates that we go on with menu tracking.
2582 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2584 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2586 if (hPtMenu)
2588 UINT id = 0;
2589 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2590 MENUITEM *item;
2592 if( IS_SYSTEM_MENU(ptmenu) )
2593 item = ptmenu->items;
2594 else
2595 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2597 if( item && (ptmenu->FocusedItem == id ))
2599 debug_print_menuitem ("FocusedItem: ", item, "");
2601 if( !(item->fType & MF_POPUP) )
2603 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2604 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2605 return executedMenuId;
2608 /* If we are dealing with the top-level menu */
2609 /* and this is a click on an already "popped" item: */
2610 /* Stop the menu tracking and close the opened submenus */
2611 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2612 return 0;
2614 ptmenu->bTimeToHide = TRUE;
2616 return -1;
2620 /***********************************************************************
2621 * MENU_MouseMove
2623 * Return TRUE if we can go on with menu tracking.
2625 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2627 UINT id = NO_SELECTED_ITEM;
2628 POPUPMENU *ptmenu = NULL;
2630 if( hPtMenu )
2632 ptmenu = MENU_GetMenu( hPtMenu );
2633 if( IS_SYSTEM_MENU(ptmenu) )
2634 id = 0;
2635 else
2636 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2639 if( id == NO_SELECTED_ITEM )
2641 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2642 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2645 else if( ptmenu->FocusedItem != id )
2647 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2648 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2650 return TRUE;
2654 /***********************************************************************
2655 * MENU_DoNextMenu
2657 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2659 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2661 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2662 BOOL atEnd = FALSE;
2664 /* When skipping left, we need to do something special after the
2665 first menu. */
2666 if (vk == VK_LEFT && menu->FocusedItem == 0)
2668 atEnd = TRUE;
2670 /* When skipping right, for the non-system menu, we need to
2671 handle the last non-special menu item (ie skip any window
2672 icons such as MDI maximize, restore or close) */
2673 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2675 UINT i = menu->FocusedItem + 1;
2676 while (i < menu->nItems) {
2677 if ((menu->items[i].wID >= SC_SIZE &&
2678 menu->items[i].wID <= SC_RESTORE)) {
2679 i++;
2680 } else break;
2682 if (i == menu->nItems) {
2683 atEnd = TRUE;
2686 /* When skipping right, we need to cater for the system menu */
2687 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2689 if (menu->FocusedItem == (menu->nItems - 1)) {
2690 atEnd = TRUE;
2694 if( atEnd )
2696 MDINEXTMENU next_menu;
2697 HMENU hNewMenu;
2698 HWND hNewWnd;
2699 UINT id = 0;
2701 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2702 next_menu.hmenuNext = 0;
2703 next_menu.hwndNext = 0;
2704 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2706 TRACE("%p [%p] -> %p [%p]\n",
2707 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2709 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2711 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2712 hNewWnd = pmt->hOwnerWnd;
2713 if( IS_SYSTEM_MENU(menu) )
2715 /* switch to the menu bar */
2717 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2719 if( vk == VK_LEFT )
2721 menu = MENU_GetMenu( hNewMenu );
2722 id = menu->nItems - 1;
2724 /* Skip backwards over any system predefined icons,
2725 eg. MDI close, restore etc icons */
2726 while ((id > 0) &&
2727 (menu->items[id].wID >= SC_SIZE &&
2728 menu->items[id].wID <= SC_RESTORE)) id--;
2731 else if (style & WS_SYSMENU )
2733 /* switch to the system menu */
2734 hNewMenu = get_win_sys_menu( hNewWnd );
2736 else return FALSE;
2738 else /* application returned a new menu to switch to */
2740 hNewMenu = next_menu.hmenuNext;
2741 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2743 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2745 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2747 if (style & WS_SYSMENU &&
2748 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2750 /* get the real system menu */
2751 hNewMenu = get_win_sys_menu(hNewWnd);
2753 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2755 /* FIXME: Not sure what to do here;
2756 * perhaps try to track hNewMenu as a popup? */
2758 TRACE(" -- got confused.\n");
2759 return FALSE;
2762 else return FALSE;
2765 if( hNewMenu != pmt->hTopMenu )
2767 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2768 FALSE, 0 );
2769 if( pmt->hCurrentMenu != pmt->hTopMenu )
2770 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2773 if( hNewWnd != pmt->hOwnerWnd )
2775 pmt->hOwnerWnd = hNewWnd;
2776 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2779 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2780 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2782 return TRUE;
2784 return FALSE;
2787 /***********************************************************************
2788 * MENU_SuspendPopup
2790 * The idea is not to show the popup if the next input message is
2791 * going to hide it anyway.
2793 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2795 MSG msg;
2797 msg.hwnd = pmt->hOwnerWnd;
2799 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2800 pmt->trackFlags |= TF_SKIPREMOVE;
2802 switch( uMsg )
2804 case WM_KEYDOWN:
2805 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2806 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2808 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2809 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2810 if( msg.message == WM_KEYDOWN &&
2811 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2813 pmt->trackFlags |= TF_SUSPENDPOPUP;
2814 return TRUE;
2817 break;
2820 /* failures go through this */
2821 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2822 return FALSE;
2825 /***********************************************************************
2826 * MENU_KeyEscape
2828 * Handle a VK_ESCAPE key event in a menu.
2830 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2832 BOOL bEndMenu = TRUE;
2834 if (pmt->hCurrentMenu != pmt->hTopMenu)
2836 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2838 if (menu->wFlags & MF_POPUP)
2840 HMENU hmenutmp, hmenuprev;
2842 hmenuprev = hmenutmp = pmt->hTopMenu;
2844 /* close topmost popup */
2845 while (hmenutmp != pmt->hCurrentMenu)
2847 hmenuprev = hmenutmp;
2848 hmenutmp = MENU_GetSubPopup( hmenuprev );
2851 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2852 pmt->hCurrentMenu = hmenuprev;
2853 bEndMenu = FALSE;
2857 return bEndMenu;
2860 /***********************************************************************
2861 * MENU_KeyLeft
2863 * Handle a VK_LEFT key event in a menu.
2865 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2867 POPUPMENU *menu;
2868 HMENU hmenutmp, hmenuprev;
2869 UINT prevcol;
2871 hmenuprev = hmenutmp = pmt->hTopMenu;
2872 menu = MENU_GetMenu( hmenutmp );
2874 /* Try to move 1 column left (if possible) */
2875 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2876 NO_SELECTED_ITEM ) {
2878 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2879 prevcol, TRUE, 0 );
2880 return;
2883 /* close topmost popup */
2884 while (hmenutmp != pmt->hCurrentMenu)
2886 hmenuprev = hmenutmp;
2887 hmenutmp = MENU_GetSubPopup( hmenuprev );
2890 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2891 pmt->hCurrentMenu = hmenuprev;
2893 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2895 /* move menu bar selection if no more popups are left */
2897 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2898 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2900 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2902 /* A sublevel menu was displayed - display the next one
2903 * unless there is another displacement coming up */
2905 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2906 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2907 pmt->hTopMenu, TRUE, wFlags);
2913 /***********************************************************************
2914 * MENU_KeyRight
2916 * Handle a VK_RIGHT key event in a menu.
2918 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2920 HMENU hmenutmp;
2921 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2922 UINT nextcol;
2924 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2925 pmt->hCurrentMenu,
2926 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2927 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2929 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2931 /* If already displaying a popup, try to display sub-popup */
2933 hmenutmp = pmt->hCurrentMenu;
2934 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2936 /* if subpopup was displayed then we are done */
2937 if (hmenutmp != pmt->hCurrentMenu) return;
2940 /* Check to see if there's another column */
2941 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2942 NO_SELECTED_ITEM ) {
2943 TRACE("Going to %d.\n", nextcol );
2944 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2945 nextcol, TRUE, 0 );
2946 return;
2949 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2951 if( pmt->hCurrentMenu != pmt->hTopMenu )
2953 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2954 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2955 } else hmenutmp = 0;
2957 /* try to move to the next item */
2958 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2959 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2961 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2962 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2963 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2964 pmt->hTopMenu, TRUE, wFlags);
2968 static void CALLBACK release_capture( BOOL __normal )
2970 set_capture_window( 0, GUI_INMENUMODE, NULL );
2973 /***********************************************************************
2974 * MENU_TrackMenu
2976 * Menu tracking code.
2978 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2979 HWND hwnd, const RECT *lprect )
2981 MSG msg;
2982 POPUPMENU *menu;
2983 BOOL fRemove;
2984 INT executedMenuId = -1;
2985 MTRACKER mt;
2986 BOOL enterIdleSent = FALSE;
2987 HWND capture_win;
2989 mt.trackFlags = 0;
2990 mt.hCurrentMenu = hmenu;
2991 mt.hTopMenu = hmenu;
2992 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2993 mt.pt.x = x;
2994 mt.pt.y = y;
2996 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2997 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2999 fEndMenu = FALSE;
3000 if (!(menu = MENU_GetMenu( hmenu )))
3002 WARN("Invalid menu handle %p\n", hmenu);
3003 SetLastError(ERROR_INVALID_MENU_HANDLE);
3004 return FALSE;
3007 if (wFlags & TPM_BUTTONDOWN)
3009 /* Get the result in order to start the tracking or not */
3010 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3011 fEndMenu = !fRemove;
3014 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3016 /* owner may not be visible when tracking a popup, so use the menu itself */
3017 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3018 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3020 __TRY while (!fEndMenu)
3022 menu = MENU_GetMenu( mt.hCurrentMenu );
3023 if (!menu) /* sometimes happens if I do a window manager close */
3024 break;
3026 /* we have to keep the message in the queue until it's
3027 * clear that menu loop is not over yet. */
3029 for (;;)
3031 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3033 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3034 /* remove the message from the queue */
3035 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3037 else
3039 if (!enterIdleSent)
3041 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3042 enterIdleSent = TRUE;
3043 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3045 WaitMessage();
3049 /* check if EndMenu() tried to cancel us, by posting this message */
3050 if(msg.message == WM_CANCELMODE)
3052 /* we are now out of the loop */
3053 fEndMenu = TRUE;
3055 /* remove the message from the queue */
3056 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3058 /* break out of internal loop, ala ESCAPE */
3059 break;
3062 TranslateMessage( &msg );
3063 mt.pt = msg.pt;
3065 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3066 enterIdleSent=FALSE;
3068 fRemove = FALSE;
3069 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3072 * Use the mouse coordinates in lParam instead of those in the MSG
3073 * struct to properly handle synthetic messages. They are already
3074 * in screen coordinates.
3076 mt.pt.x = (short)LOWORD(msg.lParam);
3077 mt.pt.y = (short)HIWORD(msg.lParam);
3079 /* Find a menu for this mouse event */
3080 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3082 switch(msg.message)
3084 /* no WM_NC... messages in captured state */
3086 case WM_RBUTTONDBLCLK:
3087 case WM_RBUTTONDOWN:
3088 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3089 /* fall through */
3090 case WM_LBUTTONDBLCLK:
3091 case WM_LBUTTONDOWN:
3092 /* If the message belongs to the menu, removes it from the queue */
3093 /* Else, end menu tracking */
3094 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3095 fEndMenu = !fRemove;
3096 break;
3098 case WM_RBUTTONUP:
3099 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3100 /* fall through */
3101 case WM_LBUTTONUP:
3102 /* Check if a menu was selected by the mouse */
3103 if (hmenu)
3105 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3106 TRACE("executedMenuId %d\n", executedMenuId);
3108 /* End the loop if executedMenuId is an item ID */
3109 /* or if the job was done (executedMenuId = 0). */
3110 fEndMenu = fRemove = (executedMenuId != -1);
3112 /* No menu was selected by the mouse */
3113 /* if the function was called by TrackPopupMenu, continue
3114 with the menu tracking. If not, stop it */
3115 else
3116 fEndMenu = !(wFlags & TPM_POPUPMENU);
3118 break;
3120 case WM_MOUSEMOVE:
3121 /* the selected menu item must be changed every time */
3122 /* the mouse moves. */
3124 if (hmenu)
3125 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3127 } /* switch(msg.message) - mouse */
3129 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3131 fRemove = TRUE; /* Keyboard messages are always removed */
3132 switch(msg.message)
3134 case WM_KEYDOWN:
3135 case WM_SYSKEYDOWN:
3136 switch(msg.wParam)
3138 case VK_MENU:
3139 case VK_F10:
3140 fEndMenu = TRUE;
3141 break;
3143 case VK_HOME:
3144 case VK_END:
3145 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3146 NO_SELECTED_ITEM, FALSE, 0 );
3147 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3148 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3149 break;
3151 case VK_UP:
3152 case VK_DOWN: /* If on menu bar, pull-down the menu */
3154 menu = MENU_GetMenu( mt.hCurrentMenu );
3155 if (!(menu->wFlags & MF_POPUP))
3156 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3157 else /* otherwise try to move selection */
3158 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3159 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3160 break;
3162 case VK_LEFT:
3163 MENU_KeyLeft( &mt, wFlags );
3164 break;
3166 case VK_RIGHT:
3167 MENU_KeyRight( &mt, wFlags );
3168 break;
3170 case VK_ESCAPE:
3171 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3172 break;
3174 case VK_F1:
3176 HELPINFO hi;
3177 hi.cbSize = sizeof(HELPINFO);
3178 hi.iContextType = HELPINFO_MENUITEM;
3179 if (menu->FocusedItem == NO_SELECTED_ITEM)
3180 hi.iCtrlId = 0;
3181 else
3182 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3183 hi.hItemHandle = hmenu;
3184 hi.dwContextId = menu->dwContextHelpID;
3185 hi.MousePos = msg.pt;
3186 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3187 break;
3190 default:
3191 break;
3193 break; /* WM_KEYDOWN */
3195 case WM_CHAR:
3196 case WM_SYSCHAR:
3198 UINT pos;
3200 if (msg.wParam == '\r' || msg.wParam == ' ')
3202 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3203 fEndMenu = (executedMenuId != -2);
3205 break;
3208 /* Hack to avoid control chars. */
3209 /* We will find a better way real soon... */
3210 if (msg.wParam < 32) break;
3212 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3213 LOWORD(msg.wParam), FALSE );
3214 if (pos == (UINT)-2) fEndMenu = TRUE;
3215 else if (pos == (UINT)-1) MessageBeep(0);
3216 else
3218 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3219 TRUE, 0 );
3220 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3221 fEndMenu = (executedMenuId != -2);
3224 break;
3225 } /* switch(msg.message) - kbd */
3227 else
3229 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3230 DispatchMessageW( &msg );
3231 continue;
3234 if (!fEndMenu) fRemove = TRUE;
3236 /* finally remove message from the queue */
3238 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3239 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3240 else mt.trackFlags &= ~TF_SKIPREMOVE;
3242 __FINALLY( release_capture )
3244 /* If dropdown is still painted and the close box is clicked on
3245 then the menu will be destroyed as part of the DispatchMessage above.
3246 This will then invalidate the menu handle in mt.hTopMenu. We should
3247 check for this first. */
3248 if( IsMenu( mt.hTopMenu ) )
3250 menu = MENU_GetMenu( mt.hTopMenu );
3252 if( IsWindow( mt.hOwnerWnd ) )
3254 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3256 if (menu && (menu->wFlags & MF_POPUP))
3258 DestroyWindow( menu->hWnd );
3259 menu->hWnd = 0;
3261 if (!(wFlags & TPM_NONOTIFY))
3262 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3263 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3265 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3266 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3269 /* Reset the variable for hiding menu */
3270 if( menu ) menu->bTimeToHide = FALSE;
3273 /* The return value is only used by TrackPopupMenu */
3274 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3275 if (executedMenuId == -1) executedMenuId = 0;
3276 return executedMenuId;
3279 /***********************************************************************
3280 * MENU_InitTracking
3282 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3284 POPUPMENU *menu;
3286 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3288 HideCaret(0);
3290 /* This makes the menus of applications built with Delphi work.
3291 * It also enables menus to be displayed in more than one window,
3292 * but there are some bugs left that need to be fixed in this case.
3294 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3295 if (!top_popup) top_popup_hmenu = hMenu;
3297 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3298 if (!(wFlags & TPM_NONOTIFY))
3299 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3301 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3303 if (!(wFlags & TPM_NONOTIFY))
3305 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3306 /* If an app changed/recreated menu bar entries in WM_INITMENU
3307 * menu sizes will be recalculated once the menu created/shown.
3311 return TRUE;
3314 /***********************************************************************
3315 * MENU_ExitTracking
3317 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3319 TRACE("hwnd=%p\n", hWnd);
3321 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3322 ShowCaret(0);
3323 top_popup = 0;
3324 top_popup_hmenu = NULL;
3325 return TRUE;
3328 /***********************************************************************
3329 * MENU_TrackMouseMenuBar
3331 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3333 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3335 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3336 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3338 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3340 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3341 if (IsMenu(hMenu))
3343 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3345 /* fetch the window menu again, it may have changed */
3346 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3347 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3348 MENU_ExitTracking(hWnd, FALSE);
3353 /***********************************************************************
3354 * MENU_TrackKbdMenuBar
3356 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3358 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3360 UINT uItem = NO_SELECTED_ITEM;
3361 HMENU hTrackMenu;
3362 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3364 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3366 /* find window that has a menu */
3368 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3369 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3371 /* check if we have to track a system menu */
3373 hTrackMenu = GetMenu( hwnd );
3374 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3376 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3377 hTrackMenu = get_win_sys_menu( hwnd );
3378 uItem = 0;
3379 wParam |= HTSYSMENU; /* prevent item lookup */
3381 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3383 if (!IsMenu( hTrackMenu )) return;
3385 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3387 /* fetch the window menu again, it may have changed */
3388 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3390 if( wChar && wChar != ' ' )
3392 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3393 if ( uItem >= (UINT)(-2) )
3395 if( uItem == (UINT)(-1) ) MessageBeep(0);
3396 /* schedule end of menu tracking */
3397 wFlags |= TF_ENDMENU;
3398 goto track_menu;
3402 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3404 if (!(wParam & HTSYSMENU) || wChar == ' ')
3406 if( uItem == NO_SELECTED_ITEM )
3407 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3408 else
3409 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3412 track_menu:
3413 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3414 MENU_ExitTracking( hwnd, FALSE );
3417 /**********************************************************************
3418 * TrackPopupMenuEx (USER32.@)
3420 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3421 HWND hWnd, LPTPMPARAMS lpTpm )
3423 POPUPMENU *menu;
3424 BOOL ret = FALSE;
3426 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3427 hMenu, wFlags, x, y, hWnd, lpTpm,
3428 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3430 /* Parameter check */
3431 /* FIXME: this check is performed several times, here and in the called
3432 functions. That could be optimized */
3433 if (!(menu = MENU_GetMenu( hMenu )))
3435 SetLastError( ERROR_INVALID_MENU_HANDLE );
3436 return FALSE;
3439 if (IsWindow(menu->hWnd))
3441 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3442 return FALSE;
3445 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3447 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3448 if (!(wFlags & TPM_NONOTIFY))
3449 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3451 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3452 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3453 lpTpm ? &lpTpm->rcExclude : NULL );
3454 MENU_ExitTracking(hWnd, TRUE);
3456 return ret;
3459 /**********************************************************************
3460 * TrackPopupMenu (USER32.@)
3462 * Like the win32 API, the function return the command ID only if the
3463 * flag TPM_RETURNCMD is on.
3466 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3467 INT nReserved, HWND hWnd, const RECT *lpRect )
3469 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3472 /***********************************************************************
3473 * PopupMenuWndProc
3475 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3477 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3479 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3481 switch(message)
3483 case WM_CREATE:
3485 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3486 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3487 return 0;
3490 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3491 return MA_NOACTIVATE;
3493 case WM_PAINT:
3495 PAINTSTRUCT ps;
3496 BeginPaint( hwnd, &ps );
3497 MENU_DrawPopupMenu( hwnd, ps.hdc,
3498 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3499 EndPaint( hwnd, &ps );
3500 return 0;
3503 case WM_PRINTCLIENT:
3505 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3506 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3507 return 0;
3510 case WM_ERASEBKGND:
3511 return 1;
3513 case WM_DESTROY:
3514 /* zero out global pointer in case resident popup window was destroyed. */
3515 if (hwnd == top_popup) {
3516 top_popup = 0;
3517 top_popup_hmenu = NULL;
3519 break;
3521 case WM_SHOWWINDOW:
3523 if( wParam )
3525 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3527 else
3528 SetWindowLongPtrW( hwnd, 0, 0 );
3529 break;
3531 case MM_SETMENUHANDLE:
3532 SetWindowLongPtrW( hwnd, 0, wParam );
3533 break;
3535 case MM_GETMENUHANDLE:
3536 case MN_GETHMENU:
3537 return GetWindowLongPtrW( hwnd, 0 );
3539 default:
3540 return DefWindowProcW( hwnd, message, wParam, lParam );
3542 return 0;
3546 /***********************************************************************
3547 * MENU_GetMenuBarHeight
3549 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3551 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3552 INT orgX, INT orgY )
3554 HDC hdc;
3555 RECT rectBar;
3556 LPPOPUPMENU lppop;
3558 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3560 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3562 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3563 SelectObject( hdc, get_menu_font(FALSE));
3564 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3565 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3566 ReleaseDC( hwnd, hdc );
3567 return lppop->Height;
3571 /*******************************************************************
3572 * ChangeMenuA (USER32.@)
3574 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3575 UINT id, UINT flags )
3577 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3578 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3579 id, data );
3580 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3581 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3582 id, data );
3583 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3584 flags & MF_BYPOSITION ? pos : id,
3585 flags & ~MF_REMOVE );
3586 /* Default: MF_INSERT */
3587 return InsertMenuA( hMenu, pos, flags, id, data );
3591 /*******************************************************************
3592 * ChangeMenuW (USER32.@)
3594 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3595 UINT id, UINT flags )
3597 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3598 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3599 id, data );
3600 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3601 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3602 id, data );
3603 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3604 flags & MF_BYPOSITION ? pos : id,
3605 flags & ~MF_REMOVE );
3606 /* Default: MF_INSERT */
3607 return InsertMenuW( hMenu, pos, flags, id, data );
3611 /*******************************************************************
3612 * CheckMenuItem (USER32.@)
3614 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3616 MENUITEM *item;
3617 DWORD ret;
3619 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3620 ret = item->fState & MF_CHECKED;
3621 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3622 else item->fState &= ~MF_CHECKED;
3623 return ret;
3627 /**********************************************************************
3628 * EnableMenuItem (USER32.@)
3630 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3632 UINT oldflags;
3633 MENUITEM *item;
3634 POPUPMENU *menu;
3636 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3638 /* Get the Popupmenu to access the owner menu */
3639 if (!(menu = MENU_GetMenu(hMenu)))
3640 return (UINT)-1;
3642 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3643 return (UINT)-1;
3645 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3646 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3648 /* If the close item in the system menu change update the close button */
3649 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3651 if (menu->hSysMenuOwner != 0)
3653 RECT rc;
3654 POPUPMENU* parentMenu;
3656 /* Get the parent menu to access*/
3657 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3658 return (UINT)-1;
3660 /* Refresh the frame to reflect the change */
3661 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3662 rc.bottom = 0;
3663 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3667 return oldflags;
3671 /*******************************************************************
3672 * GetMenuStringA (USER32.@)
3674 INT WINAPI GetMenuStringA(
3675 HMENU hMenu, /* [in] menuhandle */
3676 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3677 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3678 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3679 UINT wFlags /* [in] MF_ flags */
3681 MENUITEM *item;
3683 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3684 if (str && nMaxSiz) str[0] = '\0';
3685 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3686 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3687 return 0;
3689 if (!item->text) return 0;
3690 if (!str || !nMaxSiz) return strlenW(item->text);
3691 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3692 str[nMaxSiz-1] = 0;
3693 TRACE("returning %s\n", debugstr_a(str));
3694 return strlen(str);
3698 /*******************************************************************
3699 * GetMenuStringW (USER32.@)
3701 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3702 LPWSTR str, INT nMaxSiz, UINT wFlags )
3704 MENUITEM *item;
3706 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3707 if (str && nMaxSiz) str[0] = '\0';
3708 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3709 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3710 return 0;
3712 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3713 if( !(item->text)) {
3714 str[0] = 0;
3715 return 0;
3717 lstrcpynW( str, item->text, nMaxSiz );
3718 TRACE("returning %s\n", debugstr_w(str));
3719 return strlenW(str);
3723 /**********************************************************************
3724 * HiliteMenuItem (USER32.@)
3726 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3727 UINT wHilite )
3729 LPPOPUPMENU menu;
3730 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3731 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3732 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3733 if (menu->FocusedItem == wItemID) return TRUE;
3734 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3735 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3736 return TRUE;
3740 /**********************************************************************
3741 * GetMenuState (USER32.@)
3743 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3745 MENUITEM *item;
3746 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3747 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3748 debug_print_menuitem (" item: ", item, "");
3749 if (item->fType & MF_POPUP)
3751 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3752 if (!menu) return -1;
3753 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3755 else
3757 /* We used to (from way back then) mask the result to 0xff. */
3758 /* I don't know why and it seems wrong as the documented */
3759 /* return flag MF_SEPARATOR is outside that mask. */
3760 return (item->fType | item->fState);
3765 /**********************************************************************
3766 * GetMenuItemCount (USER32.@)
3768 INT WINAPI GetMenuItemCount( HMENU hMenu )
3770 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3771 if (!menu) return -1;
3772 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3773 return menu->nItems;
3777 /**********************************************************************
3778 * GetMenuItemID (USER32.@)
3780 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3782 MENUITEM * lpmi;
3784 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3785 if (lpmi->fType & MF_POPUP) return -1;
3786 return lpmi->wID;
3791 /**********************************************************************
3792 * MENU_mnu2mnuii
3794 * Uses flags, id and text ptr, passed by InsertMenu() and
3795 * ModifyMenu() to setup a MenuItemInfo structure.
3797 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3798 LPMENUITEMINFOW pmii)
3800 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3801 pmii->cbSize = sizeof( MENUITEMINFOW);
3802 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3803 /* setting bitmap clears text and vice versa */
3804 if( IS_STRING_ITEM(flags)) {
3805 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3806 if( !str)
3807 flags |= MF_SEPARATOR;
3808 /* Item beginning with a backspace is a help item */
3809 /* FIXME: wrong place, this is only true in win16 */
3810 else if( *str == '\b') {
3811 flags |= MF_HELP;
3812 str++;
3814 pmii->dwTypeData = (LPWSTR)str;
3815 } else if( flags & MFT_BITMAP){
3816 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3817 pmii->hbmpItem = (HBITMAP)str;
3819 if( flags & MF_OWNERDRAW){
3820 pmii->fMask |= MIIM_DATA;
3821 pmii->dwItemData = (ULONG_PTR) str;
3823 if( flags & MF_POPUP) {
3824 pmii->fMask |= MIIM_SUBMENU;
3825 pmii->hSubMenu = (HMENU)id;
3827 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3828 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3829 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3830 pmii->wID = (UINT)id;
3834 /*******************************************************************
3835 * InsertMenuW (USER32.@)
3837 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3838 UINT_PTR id, LPCWSTR str )
3840 MENUITEM *item;
3841 MENUITEMINFOW mii;
3843 if (IS_STRING_ITEM(flags) && str)
3844 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3845 hMenu, pos, flags, id, debugstr_w(str) );
3846 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3847 hMenu, pos, flags, id, str );
3849 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3850 MENU_mnu2mnuii( flags, id, str, &mii);
3851 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3853 RemoveMenu( hMenu, pos, flags );
3854 return FALSE;
3857 item->hCheckBit = item->hUnCheckBit = 0;
3858 return TRUE;
3862 /*******************************************************************
3863 * InsertMenuA (USER32.@)
3865 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3866 UINT_PTR id, LPCSTR str )
3868 BOOL ret = FALSE;
3870 if (IS_STRING_ITEM(flags) && str)
3872 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3873 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3874 if (newstr)
3876 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3877 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3878 HeapFree( GetProcessHeap(), 0, newstr );
3880 return ret;
3882 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3886 /*******************************************************************
3887 * AppendMenuA (USER32.@)
3889 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3890 UINT_PTR id, LPCSTR data )
3892 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3896 /*******************************************************************
3897 * AppendMenuW (USER32.@)
3899 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3900 UINT_PTR id, LPCWSTR data )
3902 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3906 /**********************************************************************
3907 * RemoveMenu (USER32.@)
3909 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3911 LPPOPUPMENU menu;
3912 MENUITEM *item;
3914 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3915 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3916 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3918 /* Remove item */
3920 MENU_FreeItemData( item );
3922 if (--menu->nItems == 0)
3924 HeapFree( GetProcessHeap(), 0, menu->items );
3925 menu->items = NULL;
3927 else
3929 while(nPos < menu->nItems)
3931 *item = *(item+1);
3932 item++;
3933 nPos++;
3935 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3936 menu->nItems * sizeof(MENUITEM) );
3938 return TRUE;
3942 /**********************************************************************
3943 * DeleteMenu (USER32.@)
3945 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3947 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3948 if (!item) return FALSE;
3949 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3950 /* nPos is now the position of the item */
3951 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3952 return TRUE;
3956 /*******************************************************************
3957 * ModifyMenuW (USER32.@)
3959 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3960 UINT_PTR id, LPCWSTR str )
3962 MENUITEM *item;
3963 MENUITEMINFOW mii;
3965 if (IS_STRING_ITEM(flags))
3966 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3967 else
3968 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3970 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3971 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3972 MENU_mnu2mnuii( flags, id, str, &mii);
3973 return SetMenuItemInfo_common( item, &mii, TRUE);
3977 /*******************************************************************
3978 * ModifyMenuA (USER32.@)
3980 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3981 UINT_PTR id, LPCSTR str )
3983 BOOL ret = FALSE;
3985 if (IS_STRING_ITEM(flags) && str)
3987 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3988 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3989 if (newstr)
3991 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3992 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3993 HeapFree( GetProcessHeap(), 0, newstr );
3995 return ret;
3997 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4001 /**********************************************************************
4002 * CreatePopupMenu (USER32.@)
4004 HMENU WINAPI CreatePopupMenu(void)
4006 HMENU hmenu;
4007 POPUPMENU *menu;
4009 if (!(hmenu = CreateMenu())) return 0;
4010 menu = MENU_GetMenu( hmenu );
4011 menu->wFlags |= MF_POPUP;
4012 menu->bTimeToHide = FALSE;
4013 return hmenu;
4017 /**********************************************************************
4018 * GetMenuCheckMarkDimensions (USER.417)
4019 * GetMenuCheckMarkDimensions (USER32.@)
4021 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4023 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4027 /**********************************************************************
4028 * SetMenuItemBitmaps (USER32.@)
4030 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4031 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4033 MENUITEM *item;
4035 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4037 if (!hNewCheck && !hNewUnCheck)
4039 item->fState &= ~MF_USECHECKBITMAPS;
4041 else /* Install new bitmaps */
4043 item->hCheckBit = hNewCheck;
4044 item->hUnCheckBit = hNewUnCheck;
4045 item->fState |= MF_USECHECKBITMAPS;
4047 return TRUE;
4051 /**********************************************************************
4052 * CreateMenu (USER32.@)
4054 HMENU WINAPI CreateMenu(void)
4056 HMENU hMenu;
4057 LPPOPUPMENU menu;
4059 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4060 menu->FocusedItem = NO_SELECTED_ITEM;
4061 menu->bTimeToHide = FALSE;
4063 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4065 TRACE("return %p\n", hMenu );
4067 return hMenu;
4071 /**********************************************************************
4072 * DestroyMenu (USER32.@)
4074 BOOL WINAPI DestroyMenu( HMENU hMenu )
4076 LPPOPUPMENU lppop;
4078 TRACE("(%p)\n", hMenu);
4080 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4081 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4083 /* DestroyMenu should not destroy system menu popup owner */
4084 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4086 DestroyWindow( lppop->hWnd );
4087 lppop->hWnd = 0;
4090 if (lppop->items) /* recursively destroy submenus */
4092 int i;
4093 MENUITEM *item = lppop->items;
4094 for (i = lppop->nItems; i > 0; i--, item++)
4096 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4097 MENU_FreeItemData( item );
4099 HeapFree( GetProcessHeap(), 0, lppop->items );
4101 HeapFree( GetProcessHeap(), 0, lppop );
4102 return TRUE;
4106 /**********************************************************************
4107 * GetSystemMenu (USER32.@)
4109 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4111 WND *wndPtr = WIN_GetPtr( hWnd );
4112 HMENU retvalue = 0;
4114 if (wndPtr == WND_DESKTOP) return 0;
4115 if (wndPtr == WND_OTHER_PROCESS)
4117 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4119 else if (wndPtr)
4121 if (wndPtr->hSysMenu && bRevert)
4123 DestroyMenu(wndPtr->hSysMenu);
4124 wndPtr->hSysMenu = 0;
4127 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4128 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4130 if( wndPtr->hSysMenu )
4132 POPUPMENU *menu;
4133 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4135 /* Store the dummy sysmenu handle to facilitate the refresh */
4136 /* of the close button if the SC_CLOSE item change */
4137 menu = MENU_GetMenu(retvalue);
4138 if ( menu )
4139 menu->hSysMenuOwner = wndPtr->hSysMenu;
4141 WIN_ReleasePtr( wndPtr );
4143 return bRevert ? 0 : retvalue;
4147 /*******************************************************************
4148 * SetSystemMenu (USER32.@)
4150 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4152 WND *wndPtr = WIN_GetPtr( hwnd );
4154 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4156 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4157 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4158 WIN_ReleasePtr( wndPtr );
4159 return TRUE;
4161 return FALSE;
4165 /**********************************************************************
4166 * GetMenu (USER32.@)
4168 HMENU WINAPI GetMenu( HWND hWnd )
4170 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4171 TRACE("for %p returning %p\n", hWnd, retvalue);
4172 return retvalue;
4175 /**********************************************************************
4176 * GetMenuBarInfo (USER32.@)
4178 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4180 POPUPMENU *menu;
4181 HMENU hmenu = NULL;
4182 ATOM class_atom;
4184 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4186 switch (idObject)
4188 case OBJID_CLIENT:
4189 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4190 if (!class_atom)
4191 return FALSE;
4192 if (class_atom != POPUPMENU_CLASS_ATOM)
4194 WARN("called on invalid window: %d\n", class_atom);
4195 SetLastError(ERROR_INVALID_MENU_HANDLE);
4196 return FALSE;
4199 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4200 break;
4201 case OBJID_MENU:
4202 hmenu = GetMenu(hwnd);
4203 break;
4204 case OBJID_SYSMENU:
4205 hmenu = GetSystemMenu(hwnd, FALSE);
4206 break;
4207 default:
4208 return FALSE;
4211 if (!hmenu)
4212 return FALSE;
4214 if (pmbi->cbSize != sizeof(MENUBARINFO))
4216 SetLastError(ERROR_INVALID_PARAMETER);
4217 return FALSE;
4220 menu = MENU_GetMenu(hmenu);
4221 if (!menu)
4222 return FALSE;
4223 if (idItem < 0 || idItem > menu->nItems)
4224 return FALSE;
4226 if (!menu->Height)
4228 SetRectEmpty(&pmbi->rcBar);
4230 else if (idItem == 0)
4232 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4233 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4234 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4236 else
4238 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4241 pmbi->hMenu = hmenu;
4242 pmbi->hwndMenu = NULL;
4243 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4244 if (idItem)
4246 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4247 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4249 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4250 if (menu)
4251 pmbi->hwndMenu = menu->hWnd;
4254 else
4256 pmbi->fFocused = pmbi->fBarFocused;
4259 return TRUE;
4262 /**********************************************************************
4263 * MENU_SetMenu
4265 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4266 * SetWindowPos call that would result if SetMenu were called directly.
4268 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4270 TRACE("(%p, %p);\n", hWnd, hMenu);
4272 if (hMenu && !IsMenu(hMenu))
4274 WARN("hMenu %p is not a menu handle\n", hMenu);
4275 return FALSE;
4277 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4278 return FALSE;
4280 hWnd = WIN_GetFullHandle( hWnd );
4281 if (GetCapture() == hWnd)
4282 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4284 if (hMenu != 0)
4286 LPPOPUPMENU lpmenu;
4288 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4290 lpmenu->hWnd = hWnd;
4291 lpmenu->Height = 0; /* Make sure we recalculate the size */
4293 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4294 return TRUE;
4298 /**********************************************************************
4299 * SetMenu (USER32.@)
4301 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4303 if(!MENU_SetMenu(hWnd, hMenu))
4304 return FALSE;
4306 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4307 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4308 return TRUE;
4312 /**********************************************************************
4313 * GetSubMenu (USER32.@)
4315 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4317 MENUITEM * lpmi;
4319 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4320 if (!(lpmi->fType & MF_POPUP)) return 0;
4321 return lpmi->hSubMenu;
4325 /**********************************************************************
4326 * DrawMenuBar (USER32.@)
4328 BOOL WINAPI DrawMenuBar( HWND hWnd )
4330 LPPOPUPMENU lppop;
4331 HMENU hMenu = GetMenu(hWnd);
4333 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4334 return FALSE;
4335 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4337 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4338 lppop->hwndOwner = hWnd;
4339 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4340 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4341 return TRUE;
4344 /***********************************************************************
4345 * DrawMenuBarTemp (USER32.@)
4347 * UNDOCUMENTED !!
4349 * called by W98SE desk.cpl Control Panel Applet
4351 * Not 100% sure about the param names, but close.
4353 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4355 LPPOPUPMENU lppop;
4356 UINT i,retvalue;
4357 HFONT hfontOld = 0;
4358 BOOL flat_menu = FALSE;
4360 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4362 if (!hMenu)
4363 hMenu = GetMenu(hwnd);
4365 if (!hFont)
4366 hFont = get_menu_font(FALSE);
4368 lppop = MENU_GetMenu( hMenu );
4369 if (lppop == NULL || lprect == NULL)
4371 retvalue = GetSystemMetrics(SM_CYMENU);
4372 goto END;
4375 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4377 hfontOld = SelectObject( hDC, hFont);
4379 if (lppop->Height == 0)
4380 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4382 lprect->bottom = lprect->top + lppop->Height;
4384 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4386 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4387 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4388 LineTo( hDC, lprect->right, lprect->bottom );
4390 if (lppop->nItems == 0)
4392 retvalue = GetSystemMetrics(SM_CYMENU);
4393 goto END;
4396 for (i = 0; i < lppop->nItems; i++)
4398 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4399 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4401 retvalue = lppop->Height;
4403 END:
4404 if (hfontOld) SelectObject (hDC, hfontOld);
4405 return retvalue;
4408 /***********************************************************************
4409 * EndMenu (USER.187)
4410 * EndMenu (USER32.@)
4412 BOOL WINAPI EndMenu(void)
4414 /* if we are in the menu code, and it is active */
4415 if (!fEndMenu && top_popup)
4417 /* terminate the menu handling code */
4418 fEndMenu = TRUE;
4420 /* needs to be posted to wakeup the internal menu handler */
4421 /* which will now terminate the menu, in the event that */
4422 /* the main window was minimized, or lost focus, so we */
4423 /* don't end up with an orphaned menu */
4424 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4426 return fEndMenu;
4430 /*****************************************************************
4431 * LoadMenuA (USER32.@)
4433 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4435 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4436 if (!hrsrc) return 0;
4437 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4441 /*****************************************************************
4442 * LoadMenuW (USER32.@)
4444 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4446 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4447 if (!hrsrc) return 0;
4448 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4452 /**********************************************************************
4453 * LoadMenuIndirectW (USER32.@)
4455 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4457 HMENU hMenu;
4458 WORD version, offset;
4459 LPCSTR p = template;
4461 version = GET_WORD(p);
4462 p += sizeof(WORD);
4463 TRACE("%p, ver %d\n", template, version );
4464 switch (version)
4466 case 0: /* standard format is version of 0 */
4467 offset = GET_WORD(p);
4468 p += sizeof(WORD) + offset;
4469 if (!(hMenu = CreateMenu())) return 0;
4470 if (!MENU_ParseResource( p, hMenu ))
4472 DestroyMenu( hMenu );
4473 return 0;
4475 return hMenu;
4476 case 1: /* extended format is version of 1 */
4477 offset = GET_WORD(p);
4478 p += sizeof(WORD) + offset;
4479 if (!(hMenu = CreateMenu())) return 0;
4480 if (!MENUEX_ParseResource( p, hMenu))
4482 DestroyMenu( hMenu );
4483 return 0;
4485 return hMenu;
4486 default:
4487 ERR("version %d not supported.\n", version);
4488 return 0;
4493 /**********************************************************************
4494 * LoadMenuIndirectA (USER32.@)
4496 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4498 return LoadMenuIndirectW( template );
4502 /**********************************************************************
4503 * IsMenu (USER32.@)
4505 BOOL WINAPI IsMenu(HMENU hmenu)
4507 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4509 if (!menu)
4511 SetLastError(ERROR_INVALID_MENU_HANDLE);
4512 return FALSE;
4514 return TRUE;
4517 /**********************************************************************
4518 * GetMenuItemInfo_common
4521 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4522 LPMENUITEMINFOW lpmii, BOOL unicode)
4524 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4526 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4528 if (!menu) {
4529 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4530 return FALSE;
4533 if( lpmii->fMask & MIIM_TYPE) {
4534 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4535 WARN("invalid combination of fMask bits used\n");
4536 /* this does not happen on Win9x/ME */
4537 SetLastError( ERROR_INVALID_PARAMETER);
4538 return FALSE;
4540 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4541 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4542 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4543 if( lpmii->fType & MFT_BITMAP) {
4544 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4545 lpmii->cch = 0;
4546 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4547 /* this does not happen on Win9x/ME */
4548 lpmii->dwTypeData = 0;
4549 lpmii->cch = 0;
4553 /* copy the text string */
4554 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4555 if( !menu->text ) {
4556 if(lpmii->dwTypeData && lpmii->cch) {
4557 lpmii->cch = 0;
4558 if( unicode)
4559 *((WCHAR *)lpmii->dwTypeData) = 0;
4560 else
4561 *((CHAR *)lpmii->dwTypeData) = 0;
4563 } else {
4564 int len;
4565 if (unicode)
4567 len = strlenW(menu->text);
4568 if(lpmii->dwTypeData && lpmii->cch)
4569 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4571 else
4573 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4574 0, NULL, NULL ) - 1;
4575 if(lpmii->dwTypeData && lpmii->cch)
4576 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4577 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4578 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4580 /* if we've copied a substring we return its length */
4581 if(lpmii->dwTypeData && lpmii->cch)
4582 if (lpmii->cch <= len + 1)
4583 lpmii->cch--;
4584 else
4585 lpmii->cch = len;
4586 else {
4587 /* return length of string */
4588 /* not on Win9x/ME if fType & MFT_BITMAP */
4589 lpmii->cch = len;
4594 if (lpmii->fMask & MIIM_FTYPE)
4595 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4597 if (lpmii->fMask & MIIM_BITMAP)
4598 lpmii->hbmpItem = menu->hbmpItem;
4600 if (lpmii->fMask & MIIM_STATE)
4601 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4603 if (lpmii->fMask & MIIM_ID)
4604 lpmii->wID = menu->wID;
4606 if (lpmii->fMask & MIIM_SUBMENU)
4607 lpmii->hSubMenu = menu->hSubMenu;
4608 else {
4609 /* hSubMenu is always cleared
4610 * (not on Win9x/ME ) */
4611 lpmii->hSubMenu = 0;
4614 if (lpmii->fMask & MIIM_CHECKMARKS) {
4615 lpmii->hbmpChecked = menu->hCheckBit;
4616 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4618 if (lpmii->fMask & MIIM_DATA)
4619 lpmii->dwItemData = menu->dwItemData;
4621 return TRUE;
4624 /**********************************************************************
4625 * GetMenuItemInfoA (USER32.@)
4627 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4628 LPMENUITEMINFOA lpmii)
4630 BOOL ret;
4631 MENUITEMINFOA mii;
4632 if( lpmii->cbSize != sizeof( mii) &&
4633 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4634 SetLastError( ERROR_INVALID_PARAMETER);
4635 return FALSE;
4637 memcpy( &mii, lpmii, lpmii->cbSize);
4638 mii.cbSize = sizeof( mii);
4639 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4640 (LPMENUITEMINFOW)&mii, FALSE);
4641 mii.cbSize = lpmii->cbSize;
4642 memcpy( lpmii, &mii, mii.cbSize);
4643 return ret;
4646 /**********************************************************************
4647 * GetMenuItemInfoW (USER32.@)
4649 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4650 LPMENUITEMINFOW lpmii)
4652 BOOL ret;
4653 MENUITEMINFOW mii;
4654 if( lpmii->cbSize != sizeof( mii) &&
4655 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4656 SetLastError( ERROR_INVALID_PARAMETER);
4657 return FALSE;
4659 memcpy( &mii, lpmii, lpmii->cbSize);
4660 mii.cbSize = sizeof( mii);
4661 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4662 mii.cbSize = lpmii->cbSize;
4663 memcpy( lpmii, &mii, mii.cbSize);
4664 return ret;
4668 /* set a menu item text from a ASCII or Unicode string */
4669 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4671 if (!text)
4672 menu->text = NULL;
4673 else if (unicode)
4675 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4676 strcpyW( menu->text, text );
4678 else
4680 LPCSTR str = (LPCSTR)text;
4681 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4682 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4683 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4688 /**********************************************************************
4689 * MENU_depth
4691 * detect if there are loops in the menu tree (or the depth is too large)
4693 static int MENU_depth( POPUPMENU *pmenu, int depth)
4695 UINT i;
4696 MENUITEM *item;
4697 int subdepth;
4699 depth++;
4700 if( depth > MAXMENUDEPTH) return depth;
4701 item = pmenu->items;
4702 subdepth = depth;
4703 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4704 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4705 if( psubmenu){
4706 int bdepth = MENU_depth( psubmenu, depth);
4707 if( bdepth > subdepth) subdepth = bdepth;
4709 if( subdepth > MAXMENUDEPTH)
4710 TRACE("<- hmenu %p\n", item->hSubMenu);
4712 return subdepth;
4716 /**********************************************************************
4717 * SetMenuItemInfo_common
4719 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4720 * MIIM_BITMAP and MIIM_STRING flags instead.
4723 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4724 const MENUITEMINFOW *lpmii,
4725 BOOL unicode)
4727 if (!menu) return FALSE;
4729 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4731 if (lpmii->fMask & MIIM_FTYPE ) {
4732 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4733 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4735 if (lpmii->fMask & MIIM_STRING ) {
4736 /* free the string when used */
4737 HeapFree(GetProcessHeap(), 0, menu->text);
4738 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4741 if (lpmii->fMask & MIIM_STATE)
4742 /* Other menu items having MFS_DEFAULT are not converted
4743 to normal items */
4744 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4746 if (lpmii->fMask & MIIM_ID)
4747 menu->wID = lpmii->wID;
4749 if (lpmii->fMask & MIIM_SUBMENU) {
4750 menu->hSubMenu = lpmii->hSubMenu;
4751 if (menu->hSubMenu) {
4752 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4753 if (subMenu) {
4754 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4755 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4756 menu->hSubMenu = 0;
4757 return FALSE;
4759 subMenu->wFlags |= MF_POPUP;
4760 menu->fType |= MF_POPUP;
4761 } else {
4762 SetLastError( ERROR_INVALID_PARAMETER);
4763 return FALSE;
4766 else
4767 menu->fType &= ~MF_POPUP;
4770 if (lpmii->fMask & MIIM_CHECKMARKS)
4772 menu->hCheckBit = lpmii->hbmpChecked;
4773 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4775 if (lpmii->fMask & MIIM_DATA)
4776 menu->dwItemData = lpmii->dwItemData;
4778 if (lpmii->fMask & MIIM_BITMAP)
4779 menu->hbmpItem = lpmii->hbmpItem;
4781 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4782 menu->fType |= MFT_SEPARATOR;
4784 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4785 return TRUE;
4788 /**********************************************************************
4789 * MENU_NormalizeMenuItemInfoStruct
4791 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4792 * check, copy and extend the MENUITEMINFO struct from the version that the application
4793 * supplied to the version used by wine source. */
4794 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4795 MENUITEMINFOW *pmii_out )
4797 /* do we recognize the size? */
4798 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4799 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4800 SetLastError( ERROR_INVALID_PARAMETER);
4801 return FALSE;
4803 /* copy the fields that we have */
4804 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4805 /* if the hbmpItem member is missing then extend */
4806 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4807 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4808 pmii_out->hbmpItem = NULL;
4810 /* test for invalid bit combinations */
4811 if( (pmii_out->fMask & MIIM_TYPE &&
4812 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4813 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4814 WARN("invalid combination of fMask bits used\n");
4815 /* this does not happen on Win9x/ME */
4816 SetLastError( ERROR_INVALID_PARAMETER);
4817 return FALSE;
4819 /* convert old style (MIIM_TYPE) to the new */
4820 if( pmii_out->fMask & MIIM_TYPE){
4821 pmii_out->fMask |= MIIM_FTYPE;
4822 if( IS_STRING_ITEM(pmii_out->fType)){
4823 pmii_out->fMask |= MIIM_STRING;
4824 } else if( (pmii_out->fType) & MFT_BITMAP){
4825 pmii_out->fMask |= MIIM_BITMAP;
4826 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4829 return TRUE;
4832 /**********************************************************************
4833 * SetMenuItemInfoA (USER32.@)
4835 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4836 const MENUITEMINFOA *lpmii)
4838 MENUITEMINFOW mii;
4840 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4842 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4844 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4845 &mii, FALSE);
4848 /**********************************************************************
4849 * SetMenuItemInfoW (USER32.@)
4851 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4852 const MENUITEMINFOW *lpmii)
4854 MENUITEMINFOW mii;
4856 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4858 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4859 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4860 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4863 /**********************************************************************
4864 * SetMenuDefaultItem (USER32.@)
4867 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4869 UINT i;
4870 POPUPMENU *menu;
4871 MENUITEM *item;
4873 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4875 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4877 /* reset all default-item flags */
4878 item = menu->items;
4879 for (i = 0; i < menu->nItems; i++, item++)
4881 item->fState &= ~MFS_DEFAULT;
4884 /* no default item */
4885 if ( -1 == uItem)
4887 return TRUE;
4890 item = menu->items;
4891 if ( bypos )
4893 if ( uItem >= menu->nItems ) return FALSE;
4894 item[uItem].fState |= MFS_DEFAULT;
4895 return TRUE;
4897 else
4899 for (i = 0; i < menu->nItems; i++, item++)
4901 if (item->wID == uItem)
4903 item->fState |= MFS_DEFAULT;
4904 return TRUE;
4909 return FALSE;
4912 /**********************************************************************
4913 * GetMenuDefaultItem (USER32.@)
4915 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4917 POPUPMENU *menu;
4918 MENUITEM * item;
4919 UINT i = 0;
4921 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4923 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4925 /* find default item */
4926 item = menu->items;
4928 /* empty menu */
4929 if (! item) return -1;
4931 while ( !( item->fState & MFS_DEFAULT ) )
4933 i++; item++;
4934 if (i >= menu->nItems ) return -1;
4937 /* default: don't return disabled items */
4938 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4940 /* search rekursiv when needed */
4941 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4943 UINT ret;
4944 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4945 if ( -1 != ret ) return ret;
4947 /* when item not found in submenu, return the popup item */
4949 return ( bypos ) ? i : item->wID;
4954 /**********************************************************************
4955 * InsertMenuItemA (USER32.@)
4957 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4958 const MENUITEMINFOA *lpmii)
4960 MENUITEM *item;
4961 MENUITEMINFOW mii;
4963 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4965 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4967 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4968 return SetMenuItemInfo_common(item, &mii, FALSE);
4972 /**********************************************************************
4973 * InsertMenuItemW (USER32.@)
4975 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4976 const MENUITEMINFOW *lpmii)
4978 MENUITEM *item;
4979 MENUITEMINFOW mii;
4981 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4983 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4985 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4986 return SetMenuItemInfo_common(item, &mii, TRUE);
4989 /**********************************************************************
4990 * CheckMenuRadioItem (USER32.@)
4993 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4994 UINT first, UINT last, UINT check,
4995 UINT bypos)
4997 BOOL done = FALSE;
4998 UINT i;
4999 MENUITEM *mi_first = NULL, *mi_check;
5000 HMENU m_first, m_check;
5002 for (i = first; i <= last; i++)
5004 UINT pos = i;
5006 if (!mi_first)
5008 m_first = hMenu;
5009 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5010 if (!mi_first) continue;
5011 mi_check = mi_first;
5012 m_check = m_first;
5014 else
5016 m_check = hMenu;
5017 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5018 if (!mi_check) continue;
5021 if (m_first != m_check) continue;
5022 if (mi_check->fType == MFT_SEPARATOR) continue;
5024 if (i == check)
5026 mi_check->fType |= MFT_RADIOCHECK;
5027 mi_check->fState |= MFS_CHECKED;
5028 done = TRUE;
5030 else
5032 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5033 mi_check->fState &= ~MFS_CHECKED;
5037 return done;
5041 /**********************************************************************
5042 * GetMenuItemRect (USER32.@)
5044 * ATTENTION: Here, the returned values in rect are the screen
5045 * coordinates of the item just like if the menu was
5046 * always on the upper left side of the application.
5049 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5050 LPRECT rect)
5052 POPUPMENU *itemMenu;
5053 MENUITEM *item;
5054 HWND referenceHwnd;
5056 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5058 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5059 referenceHwnd = hwnd;
5061 if(!hwnd)
5063 itemMenu = MENU_GetMenu(hMenu);
5064 if (itemMenu == NULL)
5065 return FALSE;
5067 if(itemMenu->hWnd == 0)
5068 return FALSE;
5069 referenceHwnd = itemMenu->hWnd;
5072 if ((rect == NULL) || (item == NULL))
5073 return FALSE;
5075 *rect = item->rect;
5077 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5079 return TRUE;
5082 /**********************************************************************
5083 * SetMenuInfo (USER32.@)
5085 * FIXME
5086 * actually use the items to draw the menu
5087 * (recalculate and/or redraw)
5089 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5091 POPUPMENU *menu;
5092 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5094 if (lpmi->fMask & MIM_BACKGROUND)
5095 menu->hbrBack = lpmi->hbrBack;
5097 if (lpmi->fMask & MIM_HELPID)
5098 menu->dwContextHelpID = lpmi->dwContextHelpID;
5100 if (lpmi->fMask & MIM_MAXHEIGHT)
5101 menu->cyMax = lpmi->cyMax;
5103 if (lpmi->fMask & MIM_MENUDATA)
5104 menu->dwMenuData = lpmi->dwMenuData;
5106 if (lpmi->fMask & MIM_STYLE)
5107 menu->dwStyle = lpmi->dwStyle;
5109 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5110 int i;
5111 MENUITEM *item = menu->items;
5112 for( i = menu->nItems; i; i--, item++)
5113 if( item->fType & MF_POPUP)
5114 menu_SetMenuInfo( item->hSubMenu, lpmi);
5116 return TRUE;
5119 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5121 TRACE("(%p %p)\n", hMenu, lpmi);
5122 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5123 if( lpmi->fMask & MIM_STYLE) {
5124 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5125 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5126 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5128 return TRUE;
5130 SetLastError( ERROR_INVALID_PARAMETER);
5131 return FALSE;
5134 /**********************************************************************
5135 * GetMenuInfo (USER32.@)
5137 * NOTES
5138 * win98/NT5.0
5141 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5142 { POPUPMENU *menu;
5144 TRACE("(%p %p)\n", hMenu, lpmi);
5146 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5149 if (lpmi->fMask & MIM_BACKGROUND)
5150 lpmi->hbrBack = menu->hbrBack;
5152 if (lpmi->fMask & MIM_HELPID)
5153 lpmi->dwContextHelpID = menu->dwContextHelpID;
5155 if (lpmi->fMask & MIM_MAXHEIGHT)
5156 lpmi->cyMax = menu->cyMax;
5158 if (lpmi->fMask & MIM_MENUDATA)
5159 lpmi->dwMenuData = menu->dwMenuData;
5161 if (lpmi->fMask & MIM_STYLE)
5162 lpmi->dwStyle = menu->dwStyle;
5164 return TRUE;
5166 SetLastError( ERROR_INVALID_PARAMETER);
5167 return FALSE;
5171 /**********************************************************************
5172 * SetMenuContextHelpId (USER32.@)
5174 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5176 LPPOPUPMENU menu;
5178 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5180 if ((menu = MENU_GetMenu(hMenu)))
5182 menu->dwContextHelpID = dwContextHelpID;
5183 return TRUE;
5185 return FALSE;
5189 /**********************************************************************
5190 * GetMenuContextHelpId (USER32.@)
5192 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5194 LPPOPUPMENU menu;
5196 TRACE("(%p)\n", hMenu);
5198 if ((menu = MENU_GetMenu(hMenu)))
5200 return menu->dwContextHelpID;
5202 return 0;
5205 /**********************************************************************
5206 * MenuItemFromPoint (USER32.@)
5208 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5210 POPUPMENU *menu = MENU_GetMenu(hMenu);
5211 UINT pos;
5213 /*FIXME: Do we have to handle hWnd here? */
5214 if (!menu) return -1;
5215 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5216 return pos;
5220 /**********************************************************************
5221 * translate_accelerator
5223 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5224 BYTE fVirt, WORD key, WORD cmd )
5226 INT mask = 0;
5227 UINT mesg = 0;
5229 if (wParam != key) return FALSE;
5231 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5232 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5233 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5235 if (message == WM_CHAR || message == WM_SYSCHAR)
5237 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5239 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5240 goto found;
5243 else
5245 if(fVirt & FVIRTKEY)
5247 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5248 wParam, 0xff & HIWORD(lParam));
5250 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5251 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5253 else
5255 if (!(lParam & 0x01000000)) /* no special_key */
5257 if ((fVirt & FALT) && (lParam & 0x20000000))
5258 { /* ^^ ALT pressed */
5259 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5260 goto found;
5265 return FALSE;
5267 found:
5268 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5269 mesg = 1;
5270 else
5272 HMENU hMenu, hSubMenu, hSysMenu;
5273 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5275 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5276 hSysMenu = get_win_sys_menu( hWnd );
5278 /* find menu item and ask application to initialize it */
5279 /* 1. in the system menu */
5280 hSubMenu = hSysMenu;
5281 nPos = cmd;
5282 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5284 if (GetCapture())
5285 mesg = 2;
5286 if (!IsWindowEnabled(hWnd))
5287 mesg = 3;
5288 else
5290 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5291 if(hSubMenu != hSysMenu)
5293 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5294 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5295 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5297 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5300 else /* 2. in the window's menu */
5302 hSubMenu = hMenu;
5303 nPos = cmd;
5304 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5306 if (GetCapture())
5307 mesg = 2;
5308 if (!IsWindowEnabled(hWnd))
5309 mesg = 3;
5310 else
5312 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5313 if(hSubMenu != hMenu)
5315 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5316 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5317 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5319 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5324 if (mesg == 0)
5326 if (uSysStat != (UINT)-1)
5328 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5329 mesg=4;
5330 else
5331 mesg=WM_SYSCOMMAND;
5333 else
5335 if (uStat != (UINT)-1)
5337 if (IsIconic(hWnd))
5338 mesg=5;
5339 else
5341 if (uStat & (MF_DISABLED|MF_GRAYED))
5342 mesg=6;
5343 else
5344 mesg=WM_COMMAND;
5347 else
5348 mesg=WM_COMMAND;
5353 if( mesg==WM_COMMAND )
5355 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5356 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5358 else if( mesg==WM_SYSCOMMAND )
5360 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5361 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5363 else
5365 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5366 * #0: unknown (please report!)
5367 * #1: for WM_KEYUP,WM_SYSKEYUP
5368 * #2: mouse is captured
5369 * #3: window is disabled
5370 * #4: it's a disabled system menu option
5371 * #5: it's a menu option, but window is iconic
5372 * #6: it's a menu option, but disabled
5374 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5375 if(mesg==0)
5376 ERR_(accel)(" unknown reason - please report!\n");
5378 return TRUE;
5381 /**********************************************************************
5382 * TranslateAcceleratorA (USER32.@)
5383 * TranslateAccelerator (USER32.@)
5385 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5387 switch (msg->message)
5389 case WM_KEYDOWN:
5390 case WM_SYSKEYDOWN:
5391 return TranslateAcceleratorW( hWnd, hAccel, msg );
5393 case WM_CHAR:
5394 case WM_SYSCHAR:
5396 MSG msgW = *msg;
5397 char ch = LOWORD(msg->wParam);
5398 WCHAR wch;
5399 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5400 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5401 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5404 default:
5405 return 0;
5409 /**********************************************************************
5410 * TranslateAcceleratorW (USER32.@)
5412 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5414 ACCEL data[32], *ptr = data;
5415 int i, count;
5417 if (!hWnd) return 0;
5419 if (msg->message != WM_KEYDOWN &&
5420 msg->message != WM_SYSKEYDOWN &&
5421 msg->message != WM_CHAR &&
5422 msg->message != WM_SYSCHAR)
5423 return 0;
5425 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5426 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5428 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5429 if (count > sizeof(data)/sizeof(data[0]))
5431 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5433 count = CopyAcceleratorTableW( hAccel, ptr, count );
5434 for (i = 0; i < count; i++)
5436 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5437 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5438 break;
5440 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5441 return (i < count);