user32: Add workaround for removed SC_TASKLIST system menu item.
[wine.git] / dlls / user32 / menu.c
blobbb737ac6d5b7f4ab25475a1a171dba4ed46086f0
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 /* Menu item structure */
66 typedef struct {
67 /* ----------- MENUITEMINFO Stuff ----------- */
68 UINT fType; /* Item type. */
69 UINT fState; /* Item state. */
70 UINT_PTR wID; /* Item id. */
71 HMENU hSubMenu; /* Pop-up menu. */
72 HBITMAP hCheckBit; /* Bitmap when checked. */
73 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
74 LPWSTR text; /* Item text. */
75 ULONG_PTR dwItemData; /* Application defined. */
76 LPWSTR dwTypeData; /* depends on fMask */
77 HBITMAP hbmpItem; /* bitmap */
78 /* ----------- Wine stuff ----------- */
79 RECT rect; /* Item area (relative to menu window) */
80 UINT xTab; /* X position of text after Tab */
81 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
82 * bitmap */
83 } MENUITEM;
85 /* Popup menu structure */
86 typedef struct {
87 struct user_object obj;
88 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
89 WORD Width; /* Width of the whole menu */
90 WORD Height; /* Height of the whole menu */
91 UINT nItems; /* Number of items in the menu */
92 HWND hWnd; /* Window containing the menu */
93 MENUITEM *items; /* Array of menu items */
94 UINT FocusedItem; /* Currently focused item */
95 HWND hwndOwner; /* window receiving the messages for ownerdraw */
96 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
97 BOOL bScrolling; /* Scroll arrows are active */
98 UINT nScrollPos; /* Current scroll position */
99 UINT nTotalHeight; /* Total height of menu items inside menu */
100 /* ------------ MENUINFO members ------ */
101 DWORD dwStyle; /* Extended menu style */
102 UINT cyMax; /* max height of the whole menu, 0 is screen height */
103 HBRUSH hbrBack; /* brush for menu background */
104 DWORD dwContextHelpID;
105 DWORD dwMenuData; /* application defined value */
106 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
107 WORD textOffset; /* Offset of text when items have both bitmaps and text */
108 } POPUPMENU, *LPPOPUPMENU;
110 /* internal flags for menu tracking */
112 #define TF_ENDMENU 0x10000
113 #define TF_SUSPENDPOPUP 0x20000
114 #define TF_SKIPREMOVE 0x40000
116 typedef struct
118 UINT trackFlags;
119 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
120 HMENU hTopMenu; /* initial menu */
121 HWND hOwnerWnd; /* where notifications are sent */
122 POINT pt;
123 } MTRACKER;
125 #define ITEM_PREV -1
126 #define ITEM_NEXT 1
128 /* Internal MENU_TrackMenu() flags */
129 #define TPM_INTERNAL 0xF0000000
130 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
131 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
133 /* Space between 2 columns */
134 #define MENU_COL_SPACE 4
136 /* top and bottom margins for popup menus */
137 #define MENU_TOP_MARGIN 3
138 #define MENU_BOTTOM_MARGIN 2
140 /* maximum allowed depth of any branch in the menu tree.
141 * This value is slightly larger than in windows (25) to
142 * stay on the safe side. */
143 #define MAXMENUDEPTH 30
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
152 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
153 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define MENUITEMINFO_TYPE_MASK \
159 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
160 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
161 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
162 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
163 #define STATE_MASK (~TYPE_MASK)
164 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
166 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
168 static SIZE menucharsize;
169 static UINT ODitemheight; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup;
174 static HMENU top_popup_hmenu;
176 /* Flag set by EndMenu() to force an exit from menu tracking */
177 static BOOL fEndMenu = FALSE;
179 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
181 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
183 /*********************************************************************
184 * menu class descriptor
186 const struct builtin_class_descr MENU_builtin_class =
188 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
189 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
190 WINPROC_MENU, /* proc */
191 sizeof(HMENU), /* extra */
192 IDC_ARROW, /* cursor */
193 (HBRUSH)(COLOR_MENU+1) /* brush */
197 /***********************************************************************
198 * debug_print_menuitem
200 * Print a menuitem in readable form.
203 #define debug_print_menuitem(pre, mp, post) \
204 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
206 #define MENUOUT(text) \
207 TRACE("%s%s", (count++ ? "," : ""), (text))
209 #define MENUFLAG(bit,text) \
210 do { \
211 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
212 } while (0)
214 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
215 const char *postfix)
217 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
218 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
219 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
220 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
221 TRACE("%s ", prefix);
222 if (mp) {
223 UINT flags = mp->fType;
224 TRACE( "{ ID=0x%lx", mp->wID);
225 if ( mp->hSubMenu)
226 TRACE( ", Sub=%p", mp->hSubMenu);
227 if (flags) {
228 int count = 0;
229 TRACE( ", fType=");
230 MENUFLAG( MFT_SEPARATOR, "sep");
231 MENUFLAG( MFT_OWNERDRAW, "own");
232 MENUFLAG( MFT_BITMAP, "bit");
233 MENUFLAG(MF_POPUP, "pop");
234 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
235 MENUFLAG(MFT_MENUBREAK, "brk");
236 MENUFLAG(MFT_RADIOCHECK, "radio");
237 MENUFLAG(MFT_RIGHTORDER, "rorder");
238 MENUFLAG(MF_SYSMENU, "sys");
239 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
240 if (flags)
241 TRACE( "+0x%x", flags);
243 flags = mp->fState;
244 if (flags) {
245 int count = 0;
246 TRACE( ", State=");
247 MENUFLAG(MFS_GRAYED, "grey");
248 MENUFLAG(MFS_DEFAULT, "default");
249 MENUFLAG(MFS_DISABLED, "dis");
250 MENUFLAG(MFS_CHECKED, "check");
251 MENUFLAG(MFS_HILITE, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
253 MENUFLAG(MF_MOUSESELECT, "mouse");
254 if (flags)
255 TRACE( "+0x%x", flags);
257 if (mp->hCheckBit)
258 TRACE( ", Chk=%p", mp->hCheckBit);
259 if (mp->hUnCheckBit)
260 TRACE( ", Unc=%p", mp->hUnCheckBit);
261 if (mp->text)
262 TRACE( ", Text=%s", debugstr_w(mp->text));
263 if (mp->dwItemData)
264 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
265 if (mp->hbmpItem)
267 if( IS_MAGIC_BITMAP(mp->hbmpItem))
268 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
269 else
270 TRACE( ", hbitmap=%p", mp->hbmpItem);
272 TRACE( " }");
273 } else
274 TRACE( "NULL");
275 TRACE(" %s\n", postfix);
278 #undef MENUOUT
279 #undef MENUFLAG
282 /***********************************************************************
283 * MENU_GetMenu
285 * Validate the given menu handle and returns the menu structure pointer.
287 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
289 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
291 if (menu == OBJ_OTHER_PROCESS)
293 WARN( "other process menu %p?\n", hMenu);
294 return NULL;
296 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
297 else WARN("invalid menu handle=%p\n", hMenu);
298 return menu;
301 /***********************************************************************
302 * get_win_sys_menu
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
308 HMENU ret = 0;
309 WND *win = WIN_GetPtr( hwnd );
310 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 ret = win->hSysMenu;
313 WIN_ReleasePtr( win );
315 return ret;
318 /***********************************************************************
319 * get_menu_font
321 static HFONT get_menu_font( BOOL bold )
323 static HFONT hMenuFont, hMenuFontBold;
325 HFONT ret = bold ? hMenuFontBold : hMenuFont;
327 if (!ret)
329 NONCLIENTMETRICSW ncm;
330 HFONT prev;
332 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
335 if (bold)
337 ncm.lfMenuFont.lfWeight += 300;
338 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
340 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
341 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
342 ret, NULL );
343 if (prev)
345 /* another thread beat us to it */
346 DeleteObject( ret );
347 ret = prev;
350 return ret;
353 /***********************************************************************
354 * get_arrow_bitmap
356 static HBITMAP get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap;
360 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
361 return arrow_bitmap;
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap;
371 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
372 return arrow_bitmap;
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap;
382 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
383 return arrow_bitmap;
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap;
393 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
394 return arrow_bitmap;
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap;
404 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
405 return arrow_bitmap;
408 /***********************************************************************
409 * MENU_CopySysPopup
411 * Return the default system menu.
413 static HMENU MENU_CopySysPopup(BOOL mdi)
415 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
416 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
417 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
419 if( hMenu ) {
420 MENUINFO minfo;
421 MENUITEMINFOW miteminfo;
422 POPUPMENU* menu = MENU_GetMenu(hMenu);
423 menu->wFlags |= MF_SYSMENU | MF_POPUP;
424 /* decorate the menu with bitmaps */
425 minfo.cbSize = sizeof( MENUINFO);
426 minfo.dwStyle = MNS_CHECKORBMP;
427 minfo.fMask = MIM_STYLE;
428 SetMenuInfo( hMenu, &minfo);
429 miteminfo.cbSize = sizeof( MENUITEMINFOW);
430 miteminfo.fMask = MIIM_BITMAP;
431 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
432 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
433 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
434 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
435 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
436 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
437 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
438 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
439 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
441 else
442 ERR("Unable to load default system menu\n" );
444 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
446 return hMenu;
450 /**********************************************************************
451 * MENU_GetSysMenu
453 * Create a copy of the system menu. System menu in Windows is
454 * a special menu bar with the single entry - system menu popup.
455 * This popup is presented to the outside world as a "system menu".
456 * However, the real system menu handle is sometimes seen in the
457 * WM_MENUSELECT parameters (and Word 6 likes it this way).
459 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
461 HMENU hMenu;
463 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
464 if ((hMenu = CreateMenu()))
466 POPUPMENU *menu = MENU_GetMenu(hMenu);
467 menu->wFlags = MF_SYSMENU;
468 menu->hWnd = WIN_GetFullHandle( hWnd );
469 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
471 if (!hPopupMenu)
473 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
474 hPopupMenu = MENU_CopySysPopup(TRUE);
475 else
476 hPopupMenu = MENU_CopySysPopup(FALSE);
479 if (hPopupMenu)
481 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
482 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
484 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
485 (UINT_PTR)hPopupMenu, NULL );
487 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
488 menu->items[0].fState = 0;
489 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
491 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
492 return hMenu;
494 DestroyMenu( hMenu );
496 ERR("failed to load system menu!\n");
497 return 0;
501 /***********************************************************************
502 * MENU_InitSysMenuPopup
504 * Grey the appropriate items in System menu.
506 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
508 BOOL gray;
510 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
511 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = ((style & WS_MAXIMIZE) != 0);
513 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
514 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
515 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
517 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
518 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
519 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
520 gray = (clsStyle & CS_NOCLOSE) != 0;
522 /* The menu item must keep its state if it's disabled */
523 if(gray)
524 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
528 /******************************************************************************
530 * UINT MENU_GetStartOfNextColumn(
531 * HMENU hMenu )
533 *****************************************************************************/
535 static UINT MENU_GetStartOfNextColumn(
536 HMENU hMenu )
538 POPUPMENU *menu = MENU_GetMenu(hMenu);
539 UINT i;
541 if(!menu)
542 return NO_SELECTED_ITEM;
544 i = menu->FocusedItem + 1;
545 if( i == NO_SELECTED_ITEM )
546 return i;
548 for( ; i < menu->nItems; ++i ) {
549 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
550 return i;
553 return NO_SELECTED_ITEM;
557 /******************************************************************************
559 * UINT MENU_GetStartOfPrevColumn(
560 * HMENU hMenu )
562 *****************************************************************************/
564 static UINT MENU_GetStartOfPrevColumn(
565 HMENU hMenu )
567 POPUPMENU *menu = MENU_GetMenu(hMenu);
568 UINT i;
570 if( !menu )
571 return NO_SELECTED_ITEM;
573 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
574 return NO_SELECTED_ITEM;
576 /* Find the start of the column */
578 for(i = menu->FocusedItem; i != 0 &&
579 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
580 --i); /* empty */
582 if(i == 0)
583 return NO_SELECTED_ITEM;
585 for(--i; i != 0; --i) {
586 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
587 break;
590 TRACE("ret %d.\n", i );
592 return i;
597 /***********************************************************************
598 * MENU_FindItem
600 * Find a menu item. Return a pointer on the item, and modifies *hmenu
601 * in case the item was in a sub-menu.
603 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
605 POPUPMENU *menu;
606 MENUITEM *fallback = NULL;
607 UINT fallback_pos = 0;
608 UINT i;
610 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
611 if (wFlags & MF_BYPOSITION)
613 if (*nPos >= menu->nItems) return NULL;
614 return &menu->items[*nPos];
616 else
618 MENUITEM *item = menu->items;
619 for (i = 0; i < menu->nItems; i++, item++)
621 if (item->fType & MF_POPUP)
623 HMENU hsubmenu = item->hSubMenu;
624 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
625 if (subitem)
627 *hmenu = hsubmenu;
628 return subitem;
630 else if (item->wID == *nPos)
632 /* fallback to this item if nothing else found */
633 fallback_pos = i;
634 fallback = item;
637 else if (item->wID == *nPos)
639 *nPos = i;
640 return item;
645 if (fallback)
646 *nPos = fallback_pos;
648 return fallback;
651 /***********************************************************************
652 * MENU_FindSubMenu
654 * Find a Sub menu. Return the position of the submenu, and modifies
655 * *hmenu in case it is found in another sub-menu.
656 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
658 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
660 POPUPMENU *menu;
661 UINT i;
662 MENUITEM *item;
663 if (((*hmenu)==(HMENU)0xffff) ||
664 (!(menu = MENU_GetMenu(*hmenu))))
665 return NO_SELECTED_ITEM;
666 item = menu->items;
667 for (i = 0; i < menu->nItems; i++, item++) {
668 if(!(item->fType & MF_POPUP)) continue;
669 if (item->hSubMenu == hSubTarget) {
670 return i;
672 else {
673 HMENU hsubmenu = item->hSubMenu;
674 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
675 if (pos != NO_SELECTED_ITEM) {
676 *hmenu = hsubmenu;
677 return pos;
681 return NO_SELECTED_ITEM;
684 /***********************************************************************
685 * MENU_FreeItemData
687 static void MENU_FreeItemData( MENUITEM* item )
689 /* delete text */
690 HeapFree( GetProcessHeap(), 0, item->text );
693 /***********************************************************************
694 * MENU_AdjustMenuItemRect
696 * Adjust menu item rectangle according to scrolling state.
698 static void
699 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
701 if (menu->bScrolling)
703 UINT arrow_bitmap_height;
704 BITMAP bmp;
706 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
707 arrow_bitmap_height = bmp.bmHeight;
708 rect->top += arrow_bitmap_height - menu->nScrollPos;
709 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
714 /***********************************************************************
715 * MENU_FindItemByCoords
717 * Find the item at the specified coordinates (screen coords). Does
718 * not work for child windows and therefore should not be called for
719 * an arbitrary system menu.
721 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
722 POINT pt, UINT *pos )
724 MENUITEM *item;
725 UINT i;
726 RECT rect;
728 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
729 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
730 else pt.x -= rect.left;
731 pt.y -= rect.top;
732 item = menu->items;
733 for (i = 0; i < menu->nItems; i++, item++)
735 rect = item->rect;
736 MENU_AdjustMenuItemRect(menu, &rect);
737 if (PtInRect(&rect, pt))
739 if (pos) *pos = i;
740 return item;
743 return NULL;
747 /***********************************************************************
748 * MENU_FindItemByKey
750 * Find the menu item selected by a key press.
751 * Return item id, -1 if none, -2 if we should close the menu.
753 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
754 WCHAR key, BOOL forceMenuChar )
756 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
758 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
760 if (hmenu)
762 POPUPMENU *menu = MENU_GetMenu( hmenu );
763 MENUITEM *item = menu->items;
764 LRESULT menuchar;
766 if( !forceMenuChar )
768 UINT i;
769 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
771 for (i = 0; i < menu->nItems; i++, item++)
773 if( item->text)
775 const WCHAR *p = item->text - 2;
778 const WCHAR *q = p + 2;
779 p = strchrW (q, '&');
780 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
782 while (p != NULL && p [1] == '&');
783 if (p && (toupperW(p[1]) == toupperW(key))) return i;
787 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
788 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
789 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
790 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
792 return (UINT)(-1);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
802 HWND hwndOwner)
804 BITMAP bm;
805 HBITMAP bmp = lpitem->hbmpItem;
807 size->cx = size->cy = 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR) bmp )
812 case (INT_PTR)HBMMENU_CALLBACK:
814 MEASUREITEMSTRUCT measItem;
815 measItem.CtlType = ODT_MENU;
816 measItem.CtlID = 0;
817 measItem.itemID = lpitem->wID;
818 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
819 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
820 measItem.itemData = lpitem->dwItemData;
821 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
822 size->cx = measItem.itemWidth;
823 size->cy = measItem.itemHeight;
824 return;
826 break;
827 case (INT_PTR)HBMMENU_SYSTEM:
828 if (lpitem->dwItemData)
830 bmp = (HBITMAP)lpitem->dwItemData;
831 break;
833 /* fall through */
834 case (INT_PTR)HBMMENU_MBAR_RESTORE:
835 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
836 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
837 case (INT_PTR)HBMMENU_MBAR_CLOSE:
838 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
839 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
840 size->cy = size->cx;
841 return;
842 case (INT_PTR)HBMMENU_POPUP_CLOSE:
843 case (INT_PTR)HBMMENU_POPUP_RESTORE:
844 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
845 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
846 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
847 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
848 return;
850 if (GetObjectW(bmp, sizeof(bm), &bm ))
852 size->cx = bm.bmWidth;
853 size->cy = bm.bmHeight;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
863 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
865 BITMAP bm;
866 DWORD rop;
867 HDC hdcMem;
868 HBITMAP bmp;
869 int w = rect->right - rect->left;
870 int h = rect->bottom - rect->top;
871 int bmp_xoffset = 0;
872 int left, top;
873 HBITMAP hbmToDraw = lpitem->hbmpItem;
874 bmp = hbmToDraw;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw))
879 UINT flags = 0;
880 WCHAR bmchr = 0;
881 RECT r;
883 switch((INT_PTR)hbmToDraw)
885 case (INT_PTR)HBMMENU_SYSTEM:
886 if (lpitem->dwItemData)
888 bmp = (HBITMAP)lpitem->dwItemData;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
891 else
893 static HBITMAP hBmpSysMenu;
895 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
896 bmp = hBmpSysMenu;
897 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset = bm.bmWidth / 2;
900 bm.bmWidth -= bmp_xoffset;
902 goto got_bitmap;
903 case (INT_PTR)HBMMENU_MBAR_RESTORE:
904 flags = DFCS_CAPTIONRESTORE;
905 break;
906 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
907 flags = DFCS_CAPTIONMIN;
908 break;
909 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
910 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
911 break;
912 case (INT_PTR)HBMMENU_MBAR_CLOSE:
913 flags = DFCS_CAPTIONCLOSE;
914 break;
915 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
916 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
917 break;
918 case (INT_PTR)HBMMENU_CALLBACK:
920 DRAWITEMSTRUCT drawItem;
921 drawItem.CtlType = ODT_MENU;
922 drawItem.CtlID = 0;
923 drawItem.itemID = lpitem->wID;
924 drawItem.itemAction = odaction;
925 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
926 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
927 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
928 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
929 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
930 drawItem.hwndItem = (HWND)hmenu;
931 drawItem.hDC = hdc;
932 drawItem.itemData = lpitem->dwItemData;
933 drawItem.rcItem = *rect;
934 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
935 return;
937 break;
938 case (INT_PTR)HBMMENU_POPUP_CLOSE:
939 bmchr = 0x72;
940 break;
941 case (INT_PTR)HBMMENU_POPUP_RESTORE:
942 bmchr = 0x32;
943 break;
944 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
945 bmchr = 0x31;
946 break;
947 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
948 bmchr = 0x30;
949 break;
950 default:
951 FIXME("Magic %p not implemented\n", hbmToDraw);
952 return;
954 if (bmchr)
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont, hfontsav;
959 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
960 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont.lfHeight = min( h, w) - 5 ;
963 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
964 hfont = CreateFontIndirectW( &logfont);
965 hfontsav = SelectObject(hdc, hfont);
966 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
967 SelectObject(hdc, hfontsav);
968 DeleteObject( hfont);
970 else
972 r = *rect;
973 InflateRect( &r, -1, -1 );
974 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
975 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
977 return;
980 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
982 got_bitmap:
983 hdcMem = CreateCompatibleDC( hdc );
984 SelectObject( hdcMem, bmp );
986 /* handle fontsize > bitmap_height */
987 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
988 left=rect->left;
989 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
990 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
991 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
992 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
993 DeleteDC( hdcMem );
997 /***********************************************************************
998 * MENU_CalcItemSize
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1003 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1005 WCHAR *p;
1006 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1007 UINT arrow_bitmap_width;
1008 BITMAP bm;
1009 INT itemheight;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1013 (menuBar ? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1016 arrow_bitmap_width = bm.bmWidth;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize.cx ) {
1020 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight = HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1028 if (lpitem->fType & MF_OWNERDRAW)
1030 MEASUREITEMSTRUCT mis;
1031 mis.CtlType = ODT_MENU;
1032 mis.CtlID = 0;
1033 mis.itemID = lpitem->wID;
1034 mis.itemData = lpitem->dwItemData;
1035 mis.itemHeight = ODitemheight;
1036 mis.itemWidth = 0;
1037 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1038 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1042 if (menuBar) {
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1046 } else
1047 lpitem->rect.bottom += mis.itemHeight;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1051 lpitem->rect.bottom-lpitem->rect.top);
1052 return;
1055 if (lpitem->fType & MF_SEPARATOR)
1057 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1058 if( !menuBar)
1059 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1060 return;
1063 itemheight = 0;
1064 lpitem->xTab = 0;
1066 if (!menuBar) {
1067 if (lpitem->hbmpItem) {
1068 SIZE size;
1070 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem->bmpsize = size;
1074 lppop->textOffset = max( lppop->textOffset, size.cx);
1075 lpitem->rect.right += size.cx + 2;
1076 itemheight = size.cy + 2;
1078 if( !(lppop->dwStyle & MNS_NOCHECK))
1079 lpitem->rect.right += check_bitmap_width;
1080 lpitem->rect.right += 4 + menucharsize.cx;
1081 lpitem->xTab = lpitem->rect.right;
1082 lpitem->rect.right += arrow_bitmap_width;
1083 } else if (lpitem->hbmpItem) { /* menuBar */
1084 SIZE size;
1086 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1087 lpitem->bmpsize = size;
1088 lpitem->rect.right += size.cx;
1089 if( lpitem->text) lpitem->rect.right += 2;
1090 itemheight = size.cy;
1093 /* it must be a text item - unless it's the system menu */
1094 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1095 HFONT hfontOld = NULL;
1096 RECT rc = lpitem->rect;
1097 LONG txtheight, txtwidth;
1099 if ( lpitem->fState & MFS_DEFAULT ) {
1100 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1102 if (menuBar) {
1103 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1104 DT_SINGLELINE|DT_CALCRECT);
1105 lpitem->rect.right += rc.right - rc.left;
1106 itemheight = max( max( itemheight, txtheight),
1107 GetSystemMetrics( SM_CYMENU) - 1);
1108 lpitem->rect.right += 2 * menucharsize.cx;
1109 } else {
1110 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1111 RECT tmprc = rc;
1112 LONG tmpheight;
1113 int n = (int)( p - lpitem->text);
1114 /* Item contains a tab (only meaningful in popup menus) */
1115 /* get text size before the tab */
1116 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1117 DT_SINGLELINE|DT_CALCRECT);
1118 txtwidth = rc.right - rc.left;
1119 p += 1; /* advance past the Tab */
1120 /* get text size after the tab */
1121 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1122 DT_SINGLELINE|DT_CALCRECT);
1123 lpitem->xTab += txtwidth;
1124 txtheight = max( txtheight, tmpheight);
1125 txtwidth += menucharsize.cx + /* space for the tab */
1126 tmprc.right - tmprc.left; /* space for the short cut */
1127 } else {
1128 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 txtwidth = rc.right - rc.left;
1131 lpitem->xTab += txtwidth;
1133 lpitem->rect.right += 2 + txtwidth;
1134 itemheight = max( itemheight,
1135 max( txtheight + 2, menucharsize.cy + 4));
1137 if (hfontOld) SelectObject (hdc, hfontOld);
1138 } else if( menuBar) {
1139 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1141 lpitem->rect.bottom += itemheight;
1142 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1146 /***********************************************************************
1147 * MENU_GetMaxPopupHeight
1149 static UINT
1150 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1152 if (lppop->cyMax)
1153 return lppop->cyMax;
1154 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1158 /***********************************************************************
1159 * MENU_PopupMenuCalcSize
1161 * Calculate the size of a popup menu.
1163 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1165 MENUITEM *lpitem;
1166 HDC hdc;
1167 UINT start, i;
1168 BOOL textandbmp = FALSE;
1169 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1171 lppop->Width = lppop->Height = 0;
1172 if (lppop->nItems == 0) return;
1173 hdc = GetDC( 0 );
1175 SelectObject( hdc, get_menu_font(FALSE));
1177 start = 0;
1178 maxX = 2 + 1;
1180 lppop->textOffset = 0;
1182 while (start < lppop->nItems)
1184 lpitem = &lppop->items[start];
1185 orgX = maxX;
1186 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1187 orgX += MENU_COL_SPACE;
1188 orgY = MENU_TOP_MARGIN;
1190 maxTab = maxTabWidth = 0;
1191 /* Parse items until column break or end of menu */
1192 for (i = start; i < lppop->nItems; i++, lpitem++)
1194 if ((i != start) &&
1195 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1197 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1198 maxX = max( maxX, lpitem->rect.right );
1199 orgY = lpitem->rect.bottom;
1200 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1202 maxTab = max( maxTab, lpitem->xTab );
1203 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1205 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX = max( maxX, maxTab + maxTabWidth );
1210 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1212 lpitem->rect.right = maxX;
1213 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1214 lpitem->xTab = maxTab;
1217 lppop->Height = max( lppop->Height, orgY );
1220 lppop->Width = maxX;
1221 /* if none of the items have both text and bitmap then
1222 * the text and bitmaps are all aligned on the left. If there is at
1223 * least one item with both text and bitmap then bitmaps are
1224 * on the left and texts left aligned with the right hand side
1225 * of the bitmaps */
1226 if( !textandbmp) lppop->textOffset = 0;
1228 /* space for 3d border */
1229 lppop->Height += MENU_BOTTOM_MARGIN;
1230 lppop->Width += 2;
1232 /* Adjust popup height if it exceeds maximum */
1233 maxHeight = MENU_GetMaxPopupHeight(lppop);
1234 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1235 if (lppop->Height >= maxHeight)
1237 lppop->Height = maxHeight;
1238 lppop->bScrolling = TRUE;
1240 else
1242 lppop->bScrolling = FALSE;
1245 ReleaseDC( 0, hdc );
1249 /***********************************************************************
1250 * MENU_MenuBarCalcSize
1252 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1253 * height is off by 1 pixel which causes lengthy window relocations when
1254 * active document window is maximized/restored.
1256 * Calculate the size of the menu bar.
1258 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1259 LPPOPUPMENU lppop, HWND hwndOwner )
1261 MENUITEM *lpitem;
1262 UINT start, i, helpPos;
1263 int orgX, orgY, maxY;
1265 if ((lprect == NULL) || (lppop == NULL)) return;
1266 if (lppop->nItems == 0) return;
1267 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1268 lppop->Width = lprect->right - lprect->left;
1269 lppop->Height = 0;
1270 maxY = lprect->top+1;
1271 start = 0;
1272 helpPos = ~0U;
1273 lppop->textOffset = 0;
1274 while (start < lppop->nItems)
1276 lpitem = &lppop->items[start];
1277 orgX = lprect->left;
1278 orgY = maxY;
1280 /* Parse items until line break or end of menu */
1281 for (i = start; i < lppop->nItems; i++, lpitem++)
1283 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1284 if ((i != start) &&
1285 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1287 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1288 debug_print_menuitem (" item: ", lpitem, "");
1289 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1291 if (lpitem->rect.right > lprect->right)
1293 if (i != start) break;
1294 else lpitem->rect.right = lprect->right;
1296 maxY = max( maxY, lpitem->rect.bottom );
1297 orgX = lpitem->rect.right;
1300 /* Finish the line (set all items to the largest height found) */
1301 while (start < i) lppop->items[start++].rect.bottom = maxY;
1304 lprect->bottom = maxY;
1305 lppop->Height = lprect->bottom - lprect->top;
1307 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1308 /* the last item (if several lines, only move the last line) */
1309 if (helpPos == ~0U) return;
1310 lpitem = &lppop->items[lppop->nItems-1];
1311 orgY = lpitem->rect.top;
1312 orgX = lprect->right;
1313 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1314 if (lpitem->rect.top != orgY) break; /* Other line */
1315 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1316 lpitem->rect.left += orgX - lpitem->rect.right;
1317 lpitem->rect.right = orgX;
1318 orgX = lpitem->rect.left;
1323 /***********************************************************************
1324 * MENU_DrawScrollArrows
1326 * Draw scroll arrows.
1328 static void
1329 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1331 HDC hdcMem = CreateCompatibleDC(hdc);
1332 HBITMAP hOrigBitmap;
1333 UINT arrow_bitmap_width, arrow_bitmap_height;
1334 BITMAP bmp;
1335 RECT rect;
1337 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1338 arrow_bitmap_width = bmp.bmWidth;
1339 arrow_bitmap_height = bmp.bmHeight;
1342 if (lppop->nScrollPos)
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1344 else
1345 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1346 rect.left = 0;
1347 rect.top = 0;
1348 rect.right = lppop->Width;
1349 rect.bottom = arrow_bitmap_height;
1350 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1351 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1352 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1353 rect.top = lppop->Height - arrow_bitmap_height;
1354 rect.bottom = lppop->Height;
1355 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1356 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1357 SelectObject(hdcMem, get_down_arrow_bitmap());
1358 else
1359 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1360 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1361 lppop->Height - arrow_bitmap_height,
1362 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1363 SelectObject(hdcMem, hOrigBitmap);
1364 DeleteDC(hdcMem);
1368 /***********************************************************************
1369 * draw_popup_arrow
1371 * Draws the popup-menu arrow.
1373 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1374 UINT arrow_bitmap_height)
1376 HDC hdcMem = CreateCompatibleDC( hdc );
1377 HBITMAP hOrigBitmap;
1379 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1380 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1381 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1382 arrow_bitmap_width, arrow_bitmap_height,
1383 hdcMem, 0, 0, SRCCOPY );
1384 SelectObject( hdcMem, hOrigBitmap );
1385 DeleteDC( hdcMem );
1387 /***********************************************************************
1388 * MENU_DrawMenuItem
1390 * Draw a single menu item.
1392 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1393 UINT height, BOOL menuBar, UINT odaction )
1395 RECT rect;
1396 BOOL flat_menu = FALSE;
1397 int bkgnd;
1398 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1399 POPUPMENU *menu = MENU_GetMenu(hmenu);
1400 RECT bmprc;
1402 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1404 if (!menuBar) {
1405 BITMAP bmp;
1406 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1407 arrow_bitmap_width = bmp.bmWidth;
1408 arrow_bitmap_height = bmp.bmHeight;
1411 if (lpitem->fType & MF_SYSMENU)
1413 if( !IsIconic(hwnd) )
1414 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1415 return;
1418 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1419 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1421 /* Setup colors */
1423 if (lpitem->fState & MF_HILITE)
1425 if(menuBar && !flat_menu) {
1426 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1427 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1428 } else {
1429 if(lpitem->fState & MF_GRAYED)
1430 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1431 else
1432 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1433 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1436 else
1438 if (lpitem->fState & MF_GRAYED)
1439 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1440 else
1441 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1442 SetBkColor( hdc, GetSysColor( bkgnd ) );
1445 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1446 rect = lpitem->rect;
1447 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1449 if (lpitem->fType & MF_OWNERDRAW)
1452 ** Experimentation under Windows reveals that an owner-drawn
1453 ** menu is given the rectangle which includes the space it requested
1454 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1455 ** and a popup-menu arrow. This is the value of lpitem->rect.
1456 ** Windows will leave all drawing to the application except for
1457 ** the popup-menu arrow. Windows always draws that itself, after
1458 ** the menu owner has finished drawing.
1460 DRAWITEMSTRUCT dis;
1461 COLORREF old_bk, old_text;
1463 dis.CtlType = ODT_MENU;
1464 dis.CtlID = 0;
1465 dis.itemID = lpitem->wID;
1466 dis.itemData = lpitem->dwItemData;
1467 dis.itemState = 0;
1468 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1469 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1470 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1471 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1472 dis.hwndItem = (HWND)hmenu;
1473 dis.hDC = hdc;
1474 dis.rcItem = rect;
1475 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1476 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1477 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1478 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1479 old_bk = GetBkColor( hdc );
1480 old_text = GetTextColor( hdc );
1481 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1482 /* Draw the popup-menu arrow */
1483 SetBkColor( hdc, old_bk );
1484 SetTextColor( hdc, old_text );
1485 if (lpitem->fType & MF_POPUP)
1486 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1487 arrow_bitmap_height);
1488 return;
1491 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1493 if (lpitem->fState & MF_HILITE)
1495 if (flat_menu)
1497 InflateRect (&rect, -1, -1);
1498 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1499 InflateRect (&rect, 1, 1);
1500 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1502 else
1504 if(menuBar)
1505 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1506 else
1507 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1510 else
1511 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1513 SetBkMode( hdc, TRANSPARENT );
1515 /* vertical separator */
1516 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1518 HPEN oldPen;
1519 RECT rc = rect;
1521 rc.left -= MENU_COL_SPACE / 2 + 1;
1522 rc.top = 3;
1523 rc.bottom = height - 3;
1524 if (flat_menu)
1526 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1527 MoveToEx( hdc, rc.left, rc.top, NULL );
1528 LineTo( hdc, rc.left, rc.bottom );
1529 SelectObject( hdc, oldPen );
1531 else
1532 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1535 /* horizontal separator */
1536 if (lpitem->fType & MF_SEPARATOR)
1538 HPEN oldPen;
1539 RECT rc = rect;
1541 rc.left++;
1542 rc.right--;
1543 rc.top = ( rc.top + rc.bottom) / 2;
1544 if (flat_menu)
1546 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1547 MoveToEx( hdc, rc.left, rc.top, NULL );
1548 LineTo( hdc, rc.right, rc.top );
1549 SelectObject( hdc, oldPen );
1551 else
1552 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1553 return;
1556 /* helper lines for debugging */
1557 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1558 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1559 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1560 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1563 if (lpitem->hbmpItem) {
1564 /* calculate the bitmap rectangle in coordinates relative
1565 * to the item rectangle */
1566 if( menuBar) {
1567 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1568 bmprc.left = 3;
1569 else
1570 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1572 else if (menu->dwStyle & MNS_NOCHECK)
1573 bmprc.left = 4;
1574 else if (menu->dwStyle & MNS_CHECKORBMP)
1575 bmprc.left = 2;
1576 else
1577 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1578 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1579 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1580 bmprc.top = 0;
1581 else
1582 bmprc.top = (rect.bottom - rect.top -
1583 lpitem->bmpsize.cy) / 2;
1584 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1587 if (!menuBar)
1589 HBITMAP bm;
1590 INT y = rect.top + rect.bottom;
1591 RECT rc = rect;
1592 BOOL checked = FALSE;
1593 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1594 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1595 /* Draw the check mark
1597 * FIXME:
1598 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1600 if( !(menu->dwStyle & MNS_NOCHECK)) {
1601 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1602 lpitem->hUnCheckBit;
1603 if (bm) /* we have a custom bitmap */
1605 HDC hdcMem = CreateCompatibleDC( hdc );
1607 SelectObject( hdcMem, bm );
1608 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1609 check_bitmap_width, check_bitmap_height,
1610 hdcMem, 0, 0, SRCCOPY );
1611 DeleteDC( hdcMem );
1612 checked = TRUE;
1614 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1616 RECT r;
1617 HBITMAP bm = CreateBitmap( check_bitmap_width,
1618 check_bitmap_height, 1, 1, NULL );
1619 HDC hdcMem = CreateCompatibleDC( hdc );
1621 SelectObject( hdcMem, bm );
1622 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1623 DrawFrameControl( hdcMem, &r, DFC_MENU,
1624 (lpitem->fType & MFT_RADIOCHECK) ?
1625 DFCS_MENUBULLET : DFCS_MENUCHECK );
1626 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1627 hdcMem, 0, 0, SRCCOPY );
1628 DeleteDC( hdcMem );
1629 DeleteObject( bm );
1630 checked = TRUE;
1633 if( lpitem->hbmpItem &&
1634 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1635 POINT origorg;
1636 /* some applications make this assumption on the DC's origin */
1637 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1638 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1639 odaction, FALSE);
1640 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1642 /* Draw the popup-menu arrow */
1643 if (lpitem->fType & MF_POPUP)
1644 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1645 arrow_bitmap_height);
1646 rect.left += 4;
1647 if( !(menu->dwStyle & MNS_NOCHECK))
1648 rect.left += check_bitmap_width;
1649 rect.right -= arrow_bitmap_width;
1651 else if( lpitem->hbmpItem)
1652 { /* Draw the bitmap */
1653 POINT origorg;
1655 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1656 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1657 odaction, menuBar);
1658 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1660 /* process text if present */
1661 if (lpitem->text)
1663 int i;
1664 HFONT hfontOld = 0;
1666 UINT uFormat = (menuBar) ?
1667 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1668 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1670 if( !(menu->dwStyle & MNS_CHECKORBMP))
1671 rect.left += menu->textOffset;
1673 if ( lpitem->fState & MFS_DEFAULT )
1675 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1678 if (menuBar) {
1679 if( lpitem->hbmpItem)
1680 rect.left += lpitem->bmpsize.cx;
1681 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1682 rect.left += menucharsize.cx;
1683 rect.right -= menucharsize.cx;
1686 for (i = 0; lpitem->text[i]; i++)
1687 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1688 break;
1690 if(lpitem->fState & MF_GRAYED)
1692 if (!(lpitem->fState & MF_HILITE) )
1694 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1695 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1696 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1697 --rect.left; --rect.top; --rect.right; --rect.bottom;
1699 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1702 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1704 /* paint the shortcut text */
1705 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1707 if (lpitem->text[i] == '\t')
1709 rect.left = lpitem->xTab;
1710 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1712 else
1714 rect.right = lpitem->xTab;
1715 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1718 if(lpitem->fState & MF_GRAYED)
1720 if (!(lpitem->fState & MF_HILITE) )
1722 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1723 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1724 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1725 --rect.left; --rect.top; --rect.right; --rect.bottom;
1727 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1729 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1732 if (hfontOld)
1733 SelectObject (hdc, hfontOld);
1738 /***********************************************************************
1739 * MENU_DrawPopupMenu
1741 * Paint a popup menu.
1743 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1745 HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1746 RECT rect;
1747 POPUPMENU *menu = MENU_GetMenu( hmenu );
1749 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1751 GetClientRect( hwnd, &rect );
1753 if (menu && menu->hbrBack) brush = menu->hbrBack;
1754 if ((hPrevBrush = SelectObject( hdc, brush ))
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 BOOL flat_menu = FALSE;
1766 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1767 if (flat_menu)
1768 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1769 else
1770 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1772 if (menu)
1774 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1775 /* draw menu items */
1776 if( menu->nItems)
1778 MENUITEM *item;
1779 UINT u;
1781 item = menu->items;
1782 for( u = menu->nItems; u > 0; u--, item++)
1783 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1784 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1786 /* draw scroll arrows */
1787 if (menu->bScrolling)
1788 MENU_DrawScrollArrows(menu, hdc);
1790 } else
1792 SelectObject( hdc, hPrevBrush );
1797 /***********************************************************************
1798 * MENU_DrawMenuBar
1800 * Paint a menu bar. Returns the height of the menu bar.
1801 * called from [windows/nonclient.c]
1803 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1805 LPPOPUPMENU lppop;
1806 HMENU hMenu = GetMenu(hwnd);
1808 lppop = MENU_GetMenu( hMenu );
1809 if (lppop == NULL || lprect == NULL)
1811 return GetSystemMetrics(SM_CYMENU);
1814 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1818 /***********************************************************************
1819 * MENU_InitPopup
1821 * Popup menu initialization before WM_ENTERMENULOOP.
1823 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1825 POPUPMENU *menu;
1826 DWORD ex_style = 0;
1828 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1830 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1832 /* store the owner for DrawItem */
1833 if (!IsWindow( hwndOwner ))
1835 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1836 return FALSE;
1838 menu->hwndOwner = hwndOwner;
1840 if (flags & TPM_LAYOUTRTL)
1841 ex_style = WS_EX_LAYOUTRTL;
1843 /* NOTE: In Windows, top menu popup is not owned. */
1844 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1845 WS_POPUP, 0, 0, 0, 0,
1846 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1847 (LPVOID)hmenu );
1848 if( !menu->hWnd ) return FALSE;
1849 return TRUE;
1853 /***********************************************************************
1854 * MENU_ShowPopup
1856 * Display a popup menu.
1858 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1859 INT x, INT y, INT xanchor, INT yanchor )
1861 POPUPMENU *menu;
1862 INT width, height;
1863 POINT pt;
1864 HMONITOR monitor;
1865 MONITORINFO info;
1867 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1868 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1870 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1871 if (menu->FocusedItem != NO_SELECTED_ITEM)
1873 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1874 menu->FocusedItem = NO_SELECTED_ITEM;
1877 menu->nScrollPos = 0;
1878 MENU_PopupMenuCalcSize( menu );
1880 /* adjust popup menu pos so that it fits within the desktop */
1882 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1883 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1885 /* FIXME: should use item rect */
1886 pt.x = x;
1887 pt.y = y;
1888 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1889 info.cbSize = sizeof(info);
1890 GetMonitorInfoW( monitor, &info );
1892 if (flags & TPM_LAYOUTRTL)
1893 flags ^= TPM_RIGHTALIGN;
1895 if( flags & TPM_RIGHTALIGN ) x -= width;
1896 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1898 if( flags & TPM_BOTTOMALIGN ) y -= height;
1899 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1901 if( x + width > info.rcWork.right)
1903 if( xanchor && x >= width - xanchor )
1904 x -= width - xanchor;
1906 if( x + width > info.rcWork.right)
1907 x = info.rcWork.right - width;
1909 if( x < info.rcWork.left ) x = info.rcWork.left;
1911 if( y + height > info.rcWork.bottom)
1913 if( yanchor && y >= height + yanchor )
1914 y -= height + yanchor;
1916 if( y + height > info.rcWork.bottom)
1917 y = info.rcWork.bottom - height;
1919 if( y < info.rcWork.top ) y = info.rcWork.top;
1921 if (!top_popup) {
1922 top_popup = menu->hWnd;
1923 top_popup_hmenu = hmenu;
1925 /* Display the window */
1927 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, width, height,
1928 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1929 UpdateWindow( menu->hWnd );
1930 return TRUE;
1934 /***********************************************************************
1935 * MENU_EnsureMenuItemVisible
1937 static void
1938 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1940 if (lppop->bScrolling)
1942 MENUITEM *item = &lppop->items[wIndex];
1943 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1944 UINT nOldPos = lppop->nScrollPos;
1945 RECT rc;
1946 UINT arrow_bitmap_height;
1947 BITMAP bmp;
1949 GetClientRect(lppop->hWnd, &rc);
1951 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1952 arrow_bitmap_height = bmp.bmHeight;
1954 rc.top += arrow_bitmap_height;
1955 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1957 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1958 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1961 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1962 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1963 MENU_DrawScrollArrows(lppop, hdc);
1965 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1967 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1968 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1969 MENU_DrawScrollArrows(lppop, hdc);
1975 /***********************************************************************
1976 * MENU_SelectItem
1978 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1979 BOOL sendMenuSelect, HMENU topmenu )
1981 LPPOPUPMENU lppop;
1982 HDC hdc;
1984 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1986 lppop = MENU_GetMenu( hmenu );
1987 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1989 if (lppop->FocusedItem == wIndex) return;
1990 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1991 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1992 if (!top_popup) {
1993 top_popup = lppop->hWnd;
1994 top_popup_hmenu = hmenu;
1997 SelectObject( hdc, get_menu_font(FALSE));
1999 /* Clear previous highlighted item */
2000 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2002 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2003 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2004 lppop->Height, !(lppop->wFlags & MF_POPUP),
2005 ODA_SELECT );
2008 /* Highlight new item (if any) */
2009 lppop->FocusedItem = wIndex;
2010 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2012 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2013 lppop->items[wIndex].fState |= MF_HILITE;
2014 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2015 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2016 &lppop->items[wIndex], lppop->Height,
2017 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2019 if (sendMenuSelect)
2021 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2022 SendMessageW( hwndOwner, WM_MENUSELECT,
2023 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2024 ip->fType | ip->fState |
2025 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2028 else if (sendMenuSelect) {
2029 if(topmenu){
2030 int pos;
2031 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2032 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2033 MENUITEM *ip = &ptm->items[pos];
2034 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2035 ip->fType | ip->fState |
2036 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2040 ReleaseDC( lppop->hWnd, hdc );
2044 /***********************************************************************
2045 * MENU_MoveSelection
2047 * Moves currently selected item according to the offset parameter.
2048 * If there is no selection then it should select the last item if
2049 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2051 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2053 INT i;
2054 POPUPMENU *menu;
2056 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2058 menu = MENU_GetMenu( hmenu );
2059 if ((!menu) || (!menu->items)) return;
2061 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2063 if( menu->nItems == 1 ) return; else
2064 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2065 ; i += offset)
2066 if (!(menu->items[i].fType & MF_SEPARATOR))
2068 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2069 return;
2073 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2074 i >= 0 && i < menu->nItems ; i += offset)
2075 if (!(menu->items[i].fType & MF_SEPARATOR))
2077 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2078 return;
2083 /**********************************************************************
2084 * MENU_InsertItem
2086 * Insert (allocate) a new item into a menu.
2088 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2090 MENUITEM *newItems;
2091 POPUPMENU *menu;
2093 if (!(menu = MENU_GetMenu(hMenu)))
2094 return NULL;
2096 /* Find where to insert new item */
2098 if (flags & MF_BYPOSITION) {
2099 if (pos > menu->nItems)
2100 pos = menu->nItems;
2101 } else {
2102 if (!MENU_FindItem( &hMenu, &pos, flags ))
2103 pos = menu->nItems;
2104 else {
2105 if (!(menu = MENU_GetMenu( hMenu )))
2106 return NULL;
2110 /* Make sure that MDI system buttons stay on the right side.
2111 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2112 * regardless of their id.
2114 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2115 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2116 pos--;
2118 TRACE("inserting at %u flags %x\n", pos, flags);
2120 /* Create new items array */
2122 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2123 if (!newItems)
2125 WARN("allocation failed\n" );
2126 return NULL;
2128 if (menu->nItems > 0)
2130 /* Copy the old array into the new one */
2131 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2132 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2133 (menu->nItems-pos)*sizeof(MENUITEM) );
2134 HeapFree( GetProcessHeap(), 0, menu->items );
2136 menu->items = newItems;
2137 menu->nItems++;
2138 memset( &newItems[pos], 0, sizeof(*newItems) );
2139 menu->Height = 0; /* force size recalculate */
2140 return &newItems[pos];
2144 /**********************************************************************
2145 * MENU_ParseResource
2147 * Parse a standard menu resource and add items to the menu.
2148 * Return a pointer to the end of the resource.
2150 * NOTE: flags is equivalent to the mtOption field
2152 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2154 WORD flags, id = 0;
2155 LPCWSTR str;
2156 BOOL end_flag;
2160 flags = GET_WORD(res);
2161 end_flag = flags & MF_END;
2162 /* Remove MF_END because it has the same value as MF_HILITE */
2163 flags &= ~MF_END;
2164 res += sizeof(WORD);
2165 if (!(flags & MF_POPUP))
2167 id = GET_WORD(res);
2168 res += sizeof(WORD);
2170 str = (LPCWSTR)res;
2171 res += (strlenW(str) + 1) * sizeof(WCHAR);
2172 if (flags & MF_POPUP)
2174 HMENU hSubMenu = CreatePopupMenu();
2175 if (!hSubMenu) return NULL;
2176 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2177 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2179 else /* Not a popup */
2181 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2183 } while (!end_flag);
2184 return res;
2188 /**********************************************************************
2189 * MENUEX_ParseResource
2191 * Parse an extended menu resource and add items to the menu.
2192 * Return a pointer to the end of the resource.
2194 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2196 WORD resinfo;
2197 do {
2198 MENUITEMINFOW mii;
2200 mii.cbSize = sizeof(mii);
2201 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2202 mii.fType = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 mii.fState = GET_DWORD(res);
2205 res += sizeof(DWORD);
2206 mii.wID = GET_DWORD(res);
2207 res += sizeof(DWORD);
2208 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2209 res += sizeof(WORD);
2210 /* Align the text on a word boundary. */
2211 res += (~((UINT_PTR)res - 1)) & 1;
2212 mii.dwTypeData = (LPWSTR) res;
2213 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2214 /* Align the following fields on a dword boundary. */
2215 res += (~((UINT_PTR)res - 1)) & 3;
2217 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2218 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2220 if (resinfo & 1) { /* Pop-up? */
2221 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2222 res += sizeof(DWORD);
2223 mii.hSubMenu = CreatePopupMenu();
2224 if (!mii.hSubMenu)
2225 return NULL;
2226 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2227 DestroyMenu(mii.hSubMenu);
2228 return NULL;
2230 mii.fMask |= MIIM_SUBMENU;
2231 mii.fType |= MF_POPUP;
2233 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2235 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2236 mii.wID, mii.fType);
2237 mii.fType |= MF_SEPARATOR;
2239 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2240 } while (!(resinfo & MF_END));
2241 return res;
2245 /***********************************************************************
2246 * MENU_GetSubPopup
2248 * Return the handle of the selected sub-popup menu (if any).
2250 static HMENU MENU_GetSubPopup( HMENU hmenu )
2252 POPUPMENU *menu;
2253 MENUITEM *item;
2255 menu = MENU_GetMenu( hmenu );
2257 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2259 item = &menu->items[menu->FocusedItem];
2260 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2261 return item->hSubMenu;
2262 return 0;
2266 /***********************************************************************
2267 * MENU_HideSubPopups
2269 * Hide the sub-popup menus of this menu.
2271 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2272 BOOL sendMenuSelect, UINT wFlags )
2274 POPUPMENU *menu = MENU_GetMenu( hmenu );
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2278 if (menu && top_popup)
2280 HMENU hsubmenu;
2281 POPUPMENU *submenu;
2282 MENUITEM *item;
2284 if (menu->FocusedItem != NO_SELECTED_ITEM)
2286 item = &menu->items[menu->FocusedItem];
2287 if (!(item->fType & MF_POPUP) ||
2288 !(item->fState & MF_MOUSESELECT)) return;
2289 item->fState &= ~MF_MOUSESELECT;
2290 hsubmenu = item->hSubMenu;
2291 } else return;
2293 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2294 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2295 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2296 DestroyWindow( submenu->hWnd );
2297 submenu->hWnd = 0;
2299 if (!(wFlags & TPM_NONOTIFY))
2300 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2301 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2306 /***********************************************************************
2307 * MENU_ShowSubPopup
2309 * Display the sub-menu of the selected item of this menu.
2310 * Return the handle of the submenu, or hmenu if no submenu to display.
2312 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2313 BOOL selectFirst, UINT wFlags )
2315 RECT rect;
2316 POPUPMENU *menu;
2317 MENUITEM *item;
2318 HDC hdc;
2320 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2322 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2324 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2326 item = &menu->items[menu->FocusedItem];
2327 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2328 return hmenu;
2330 /* message must be sent before using item,
2331 because nearly everything may be changed by the application ! */
2333 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2334 if (!(wFlags & TPM_NONOTIFY))
2335 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2336 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2338 item = &menu->items[menu->FocusedItem];
2339 rect = item->rect;
2341 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2342 if (!(item->fState & MF_HILITE))
2344 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2345 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2347 SelectObject( hdc, get_menu_font(FALSE));
2349 item->fState |= MF_HILITE;
2350 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2351 ReleaseDC( menu->hWnd, hdc );
2353 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2354 item->rect = rect;
2356 item->fState |= MF_MOUSESELECT;
2358 if (IS_SYSTEM_MENU(menu))
2360 MENU_InitSysMenuPopup(item->hSubMenu,
2361 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2362 GetClassLongW( menu->hWnd, GCL_STYLE));
2364 NC_GetSysPopupPos( menu->hWnd, &rect );
2365 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2366 rect.top = rect.bottom;
2367 rect.right = GetSystemMetrics(SM_CXSIZE);
2368 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2370 else
2372 GetWindowRect( menu->hWnd, &rect );
2373 if (menu->wFlags & MF_POPUP)
2375 RECT rc = item->rect;
2377 MENU_AdjustMenuItemRect(menu, &rc);
2379 /* The first item in the popup menu has to be at the
2380 same y position as the focused menu item */
2381 if (wFlags & TPM_LAYOUTRTL)
2382 rect.left += GetSystemMetrics(SM_CXBORDER);
2383 else
2384 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2385 rect.top += rc.top - MENU_TOP_MARGIN;
2386 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2387 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2388 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2390 else
2392 if (wFlags & TPM_LAYOUTRTL)
2393 rect.left = rect.right - item->rect.left;
2394 else
2395 rect.left += item->rect.left;
2396 rect.top += item->rect.bottom;
2397 rect.right = item->rect.right - item->rect.left;
2398 rect.bottom = item->rect.bottom - item->rect.top;
2402 /* use default alignment for submenus */
2403 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2405 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2407 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2408 rect.left, rect.top, rect.right, rect.bottom );
2409 if (selectFirst)
2410 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2411 return item->hSubMenu;
2416 /**********************************************************************
2417 * MENU_IsMenuActive
2419 HWND MENU_IsMenuActive(void)
2421 return top_popup;
2424 /**********************************************************************
2425 * MENU_EndMenu
2427 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2429 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2431 void MENU_EndMenu( HWND hwnd )
2433 POPUPMENU *menu;
2434 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2435 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2438 /***********************************************************************
2439 * MENU_PtMenu
2441 * Walks menu chain trying to find a menu pt maps to.
2443 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2445 POPUPMENU *menu = MENU_GetMenu( hMenu );
2446 UINT item = menu->FocusedItem;
2447 HMENU ret;
2449 /* try subpopup first (if any) */
2450 ret = (item != NO_SELECTED_ITEM &&
2451 (menu->items[item].fType & MF_POPUP) &&
2452 (menu->items[item].fState & MF_MOUSESELECT))
2453 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2455 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2457 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2458 if( menu->wFlags & MF_POPUP )
2460 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2462 else if (ht == HTSYSMENU)
2463 ret = get_win_sys_menu( menu->hWnd );
2464 else if (ht == HTMENU)
2465 ret = GetMenu( menu->hWnd );
2467 return ret;
2470 /***********************************************************************
2471 * MENU_ExecFocusedItem
2473 * Execute a menu item (for instance when user pressed Enter).
2474 * Return the wID of the executed item. Otherwise, -1 indicating
2475 * that no menu item was executed, -2 if a popup is shown;
2476 * Have to receive the flags for the TrackPopupMenu options to avoid
2477 * sending unwanted message.
2480 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2482 MENUITEM *item;
2483 POPUPMENU *menu = MENU_GetMenu( hMenu );
2485 TRACE("%p hmenu=%p\n", pmt, hMenu);
2487 if (!menu || !menu->nItems ||
2488 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2490 item = &menu->items[menu->FocusedItem];
2492 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2494 if (!(item->fType & MF_POPUP))
2496 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2498 /* If TPM_RETURNCMD is set you return the id, but
2499 do not send a message to the owner */
2500 if(!(wFlags & TPM_RETURNCMD))
2502 if( menu->wFlags & MF_SYSMENU )
2503 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2504 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2505 else
2507 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2508 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2510 if (dwStyle & MNS_NOTIFYBYPOS)
2511 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2512 (LPARAM)hMenu);
2513 else
2514 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2517 return item->wID;
2520 else
2522 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2523 return -2;
2526 return -1;
2529 /***********************************************************************
2530 * MENU_SwitchTracking
2532 * Helper function for menu navigation routines.
2534 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2536 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2537 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2539 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2541 if( pmt->hTopMenu != hPtMenu &&
2542 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2544 /* both are top level menus (system and menu-bar) */
2545 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2546 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2547 pmt->hTopMenu = hPtMenu;
2549 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2550 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2554 /***********************************************************************
2555 * MENU_ButtonDown
2557 * Return TRUE if we can go on with menu tracking.
2559 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2561 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2563 if (hPtMenu)
2565 UINT id = 0;
2566 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2567 MENUITEM *item;
2569 if( IS_SYSTEM_MENU(ptmenu) )
2570 item = ptmenu->items;
2571 else
2572 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2574 if( item )
2576 if( ptmenu->FocusedItem != id )
2577 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2579 /* If the popup menu is not already "popped" */
2580 if(!(item->fState & MF_MOUSESELECT ))
2582 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2585 return TRUE;
2587 /* Else the click was on the menu bar, finish the tracking */
2589 return FALSE;
2592 /***********************************************************************
2593 * MENU_ButtonUp
2595 * Return the value of MENU_ExecFocusedItem if
2596 * the selected item was not a popup. Else open the popup.
2597 * A -1 return value indicates that we go on with menu tracking.
2600 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2602 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2604 if (hPtMenu)
2606 UINT id = 0;
2607 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2608 MENUITEM *item;
2610 if( IS_SYSTEM_MENU(ptmenu) )
2611 item = ptmenu->items;
2612 else
2613 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2615 if( item && (ptmenu->FocusedItem == id ))
2617 debug_print_menuitem ("FocusedItem: ", item, "");
2619 if( !(item->fType & MF_POPUP) )
2621 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2622 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2623 return executedMenuId;
2626 /* If we are dealing with the menu bar */
2627 /* and this is a click on an already "popped" item: */
2628 /* Stop the menu tracking and close the opened submenus */
2629 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2630 return 0;
2632 if( GetMenu(ptmenu->hWnd) == hPtMenu )
2633 ptmenu->bTimeToHide = TRUE;
2635 return -1;
2639 /***********************************************************************
2640 * MENU_MouseMove
2642 * Return TRUE if we can go on with menu tracking.
2644 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2646 UINT id = NO_SELECTED_ITEM;
2647 POPUPMENU *ptmenu = NULL;
2649 if( hPtMenu )
2651 ptmenu = MENU_GetMenu( hPtMenu );
2652 if( IS_SYSTEM_MENU(ptmenu) )
2653 id = 0;
2654 else
2655 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2658 if( id == NO_SELECTED_ITEM )
2660 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2661 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2664 else if( ptmenu->FocusedItem != id )
2666 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2667 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2669 return TRUE;
2673 /***********************************************************************
2674 * MENU_DoNextMenu
2676 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2678 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2680 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2681 BOOL atEnd = FALSE;
2683 /* When skipping left, we need to do something special after the
2684 first menu. */
2685 if (vk == VK_LEFT && menu->FocusedItem == 0)
2687 atEnd = TRUE;
2689 /* When skipping right, for the non-system menu, we need to
2690 handle the last non-special menu item (ie skip any window
2691 icons such as MDI maximize, restore or close) */
2692 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2694 UINT i = menu->FocusedItem + 1;
2695 while (i < menu->nItems) {
2696 if ((menu->items[i].wID >= SC_SIZE &&
2697 menu->items[i].wID <= SC_RESTORE)) {
2698 i++;
2699 } else break;
2701 if (i == menu->nItems) {
2702 atEnd = TRUE;
2705 /* When skipping right, we need to cater for the system menu */
2706 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2708 if (menu->FocusedItem == (menu->nItems - 1)) {
2709 atEnd = TRUE;
2713 if( atEnd )
2715 MDINEXTMENU next_menu;
2716 HMENU hNewMenu;
2717 HWND hNewWnd;
2718 UINT id = 0;
2720 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2721 next_menu.hmenuNext = 0;
2722 next_menu.hwndNext = 0;
2723 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2725 TRACE("%p [%p] -> %p [%p]\n",
2726 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2728 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2730 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2731 hNewWnd = pmt->hOwnerWnd;
2732 if( IS_SYSTEM_MENU(menu) )
2734 /* switch to the menu bar */
2736 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2738 if( vk == VK_LEFT )
2740 menu = MENU_GetMenu( hNewMenu );
2741 id = menu->nItems - 1;
2743 /* Skip backwards over any system predefined icons,
2744 eg. MDI close, restore etc icons */
2745 while ((id > 0) &&
2746 (menu->items[id].wID >= SC_SIZE &&
2747 menu->items[id].wID <= SC_RESTORE)) id--;
2750 else if (style & WS_SYSMENU )
2752 /* switch to the system menu */
2753 hNewMenu = get_win_sys_menu( hNewWnd );
2755 else return FALSE;
2757 else /* application returned a new menu to switch to */
2759 hNewMenu = next_menu.hmenuNext;
2760 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2762 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2764 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2766 if (style & WS_SYSMENU &&
2767 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2769 /* get the real system menu */
2770 hNewMenu = get_win_sys_menu(hNewWnd);
2772 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2774 /* FIXME: Not sure what to do here;
2775 * perhaps try to track hNewMenu as a popup? */
2777 TRACE(" -- got confused.\n");
2778 return FALSE;
2781 else return FALSE;
2784 if( hNewMenu != pmt->hTopMenu )
2786 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2787 FALSE, 0 );
2788 if( pmt->hCurrentMenu != pmt->hTopMenu )
2789 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2792 if( hNewWnd != pmt->hOwnerWnd )
2794 pmt->hOwnerWnd = hNewWnd;
2795 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2798 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2799 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2801 return TRUE;
2803 return FALSE;
2806 /***********************************************************************
2807 * MENU_SuspendPopup
2809 * The idea is not to show the popup if the next input message is
2810 * going to hide it anyway.
2812 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2814 MSG msg;
2816 msg.hwnd = pmt->hOwnerWnd;
2818 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2819 pmt->trackFlags |= TF_SKIPREMOVE;
2821 switch( uMsg )
2823 case WM_KEYDOWN:
2824 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2825 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2827 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2828 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2829 if( msg.message == WM_KEYDOWN &&
2830 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2832 pmt->trackFlags |= TF_SUSPENDPOPUP;
2833 return TRUE;
2836 break;
2839 /* failures go through this */
2840 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2841 return FALSE;
2844 /***********************************************************************
2845 * MENU_KeyEscape
2847 * Handle a VK_ESCAPE key event in a menu.
2849 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2851 BOOL bEndMenu = TRUE;
2853 if (pmt->hCurrentMenu != pmt->hTopMenu)
2855 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2857 if (menu->wFlags & MF_POPUP)
2859 HMENU hmenutmp, hmenuprev;
2861 hmenuprev = hmenutmp = pmt->hTopMenu;
2863 /* close topmost popup */
2864 while (hmenutmp != pmt->hCurrentMenu)
2866 hmenuprev = hmenutmp;
2867 hmenutmp = MENU_GetSubPopup( hmenuprev );
2870 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2871 pmt->hCurrentMenu = hmenuprev;
2872 bEndMenu = FALSE;
2876 return bEndMenu;
2879 /***********************************************************************
2880 * MENU_KeyLeft
2882 * Handle a VK_LEFT key event in a menu.
2884 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2886 POPUPMENU *menu;
2887 HMENU hmenutmp, hmenuprev;
2888 UINT prevcol;
2890 hmenuprev = hmenutmp = pmt->hTopMenu;
2891 menu = MENU_GetMenu( hmenutmp );
2893 /* Try to move 1 column left (if possible) */
2894 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2895 NO_SELECTED_ITEM ) {
2897 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2898 prevcol, TRUE, 0 );
2899 return;
2902 /* close topmost popup */
2903 while (hmenutmp != pmt->hCurrentMenu)
2905 hmenuprev = hmenutmp;
2906 hmenutmp = MENU_GetSubPopup( hmenuprev );
2909 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2910 pmt->hCurrentMenu = hmenuprev;
2912 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2914 /* move menu bar selection if no more popups are left */
2916 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2917 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2919 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2921 /* A sublevel menu was displayed - display the next one
2922 * unless there is another displacement coming up */
2924 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2925 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2926 pmt->hTopMenu, TRUE, wFlags);
2932 /***********************************************************************
2933 * MENU_KeyRight
2935 * Handle a VK_RIGHT key event in a menu.
2937 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2939 HMENU hmenutmp;
2940 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2941 UINT nextcol;
2943 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2944 pmt->hCurrentMenu,
2945 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2946 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2948 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2950 /* If already displaying a popup, try to display sub-popup */
2952 hmenutmp = pmt->hCurrentMenu;
2953 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2955 /* if subpopup was displayed then we are done */
2956 if (hmenutmp != pmt->hCurrentMenu) return;
2959 /* Check to see if there's another column */
2960 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2961 NO_SELECTED_ITEM ) {
2962 TRACE("Going to %d.\n", nextcol );
2963 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2964 nextcol, TRUE, 0 );
2965 return;
2968 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2970 if( pmt->hCurrentMenu != pmt->hTopMenu )
2972 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2973 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2974 } else hmenutmp = 0;
2976 /* try to move to the next item */
2977 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2978 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2980 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2981 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2982 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2983 pmt->hTopMenu, TRUE, wFlags);
2987 static void CALLBACK release_capture( BOOL __normal )
2989 set_capture_window( 0, GUI_INMENUMODE, NULL );
2992 /***********************************************************************
2993 * MENU_TrackMenu
2995 * Menu tracking code.
2997 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2998 HWND hwnd, const RECT *lprect )
3000 MSG msg;
3001 POPUPMENU *menu;
3002 BOOL fRemove;
3003 INT executedMenuId = -1;
3004 MTRACKER mt;
3005 BOOL enterIdleSent = FALSE;
3006 HWND capture_win;
3008 mt.trackFlags = 0;
3009 mt.hCurrentMenu = hmenu;
3010 mt.hTopMenu = hmenu;
3011 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3012 mt.pt.x = x;
3013 mt.pt.y = y;
3015 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3016 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3018 if (!(menu = MENU_GetMenu( hmenu )))
3020 WARN("Invalid menu handle %p\n", hmenu);
3021 SetLastError(ERROR_INVALID_MENU_HANDLE);
3022 return FALSE;
3025 if (wFlags & TPM_BUTTONDOWN)
3027 /* Get the result in order to start the tracking or not */
3028 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3029 fEndMenu = !fRemove;
3032 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3034 /* owner may not be visible when tracking a popup, so use the menu itself */
3035 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3036 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3038 __TRY while (!fEndMenu)
3040 menu = MENU_GetMenu( mt.hCurrentMenu );
3041 if (!menu) /* sometimes happens if I do a window manager close */
3042 break;
3044 /* we have to keep the message in the queue until it's
3045 * clear that menu loop is not over yet. */
3047 for (;;)
3049 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3051 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3052 /* remove the message from the queue */
3053 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3055 else
3057 if (!enterIdleSent)
3059 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3060 enterIdleSent = TRUE;
3061 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3063 WaitMessage();
3067 /* check if EndMenu() tried to cancel us, by posting this message */
3068 if(msg.message == WM_CANCELMODE)
3070 /* we are now out of the loop */
3071 fEndMenu = TRUE;
3073 /* remove the message from the queue */
3074 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3076 /* break out of internal loop, ala ESCAPE */
3077 break;
3080 TranslateMessage( &msg );
3081 mt.pt = msg.pt;
3083 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3084 enterIdleSent=FALSE;
3086 fRemove = FALSE;
3087 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3090 * Use the mouse coordinates in lParam instead of those in the MSG
3091 * struct to properly handle synthetic messages. They are already
3092 * in screen coordinates.
3094 mt.pt.x = (short)LOWORD(msg.lParam);
3095 mt.pt.y = (short)HIWORD(msg.lParam);
3097 /* Find a menu for this mouse event */
3098 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3100 switch(msg.message)
3102 /* no WM_NC... messages in captured state */
3104 case WM_RBUTTONDBLCLK:
3105 case WM_RBUTTONDOWN:
3106 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3107 /* fall through */
3108 case WM_LBUTTONDBLCLK:
3109 case WM_LBUTTONDOWN:
3110 /* If the message belongs to the menu, removes it from the queue */
3111 /* Else, end menu tracking */
3112 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3113 fEndMenu = !fRemove;
3114 break;
3116 case WM_RBUTTONUP:
3117 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3118 /* fall through */
3119 case WM_LBUTTONUP:
3120 /* Check if a menu was selected by the mouse */
3121 if (hmenu)
3123 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3124 TRACE("executedMenuId %d\n", executedMenuId);
3126 /* End the loop if executedMenuId is an item ID */
3127 /* or if the job was done (executedMenuId = 0). */
3128 fEndMenu = fRemove = (executedMenuId != -1);
3130 /* No menu was selected by the mouse */
3131 /* if the function was called by TrackPopupMenu, continue
3132 with the menu tracking. If not, stop it */
3133 else
3134 fEndMenu = !(wFlags & TPM_POPUPMENU);
3136 break;
3138 case WM_MOUSEMOVE:
3139 /* the selected menu item must be changed every time */
3140 /* the mouse moves. */
3142 if (hmenu)
3143 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3145 } /* switch(msg.message) - mouse */
3147 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3149 fRemove = TRUE; /* Keyboard messages are always removed */
3150 switch(msg.message)
3152 case WM_KEYDOWN:
3153 case WM_SYSKEYDOWN:
3154 switch(msg.wParam)
3156 case VK_MENU:
3157 case VK_F10:
3158 fEndMenu = TRUE;
3159 break;
3161 case VK_HOME:
3162 case VK_END:
3163 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3164 NO_SELECTED_ITEM, FALSE, 0 );
3165 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3166 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3167 break;
3169 case VK_UP:
3170 case VK_DOWN: /* If on menu bar, pull-down the menu */
3172 menu = MENU_GetMenu( mt.hCurrentMenu );
3173 if (!(menu->wFlags & MF_POPUP))
3174 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3175 else /* otherwise try to move selection */
3176 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3177 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3178 break;
3180 case VK_LEFT:
3181 MENU_KeyLeft( &mt, wFlags );
3182 break;
3184 case VK_RIGHT:
3185 MENU_KeyRight( &mt, wFlags );
3186 break;
3188 case VK_ESCAPE:
3189 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3190 break;
3192 case VK_F1:
3194 HELPINFO hi;
3195 hi.cbSize = sizeof(HELPINFO);
3196 hi.iContextType = HELPINFO_MENUITEM;
3197 if (menu->FocusedItem == NO_SELECTED_ITEM)
3198 hi.iCtrlId = 0;
3199 else
3200 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3201 hi.hItemHandle = hmenu;
3202 hi.dwContextId = menu->dwContextHelpID;
3203 hi.MousePos = msg.pt;
3204 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3205 break;
3208 default:
3209 break;
3211 break; /* WM_KEYDOWN */
3213 case WM_CHAR:
3214 case WM_SYSCHAR:
3216 UINT pos;
3218 if (msg.wParam == '\r' || msg.wParam == ' ')
3220 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3221 fEndMenu = (executedMenuId != -2);
3223 break;
3226 /* Hack to avoid control chars. */
3227 /* We will find a better way real soon... */
3228 if (msg.wParam < 32) break;
3230 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3231 LOWORD(msg.wParam), FALSE );
3232 if (pos == (UINT)-2) fEndMenu = TRUE;
3233 else if (pos == (UINT)-1) MessageBeep(0);
3234 else
3236 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3237 TRUE, 0 );
3238 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3239 fEndMenu = (executedMenuId != -2);
3242 break;
3243 } /* switch(msg.message) - kbd */
3245 else
3247 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3248 DispatchMessageW( &msg );
3249 continue;
3252 if (!fEndMenu) fRemove = TRUE;
3254 /* finally remove message from the queue */
3256 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3257 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3258 else mt.trackFlags &= ~TF_SKIPREMOVE;
3260 __FINALLY( release_capture )
3262 /* If dropdown is still painted and the close box is clicked on
3263 then the menu will be destroyed as part of the DispatchMessage above.
3264 This will then invalidate the menu handle in mt.hTopMenu. We should
3265 check for this first. */
3266 if( IsMenu( mt.hTopMenu ) )
3268 menu = MENU_GetMenu( mt.hTopMenu );
3270 if( IsWindow( mt.hOwnerWnd ) )
3272 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3274 if (menu && (menu->wFlags & MF_POPUP))
3276 DestroyWindow( menu->hWnd );
3277 menu->hWnd = 0;
3279 if (!(wFlags & TPM_NONOTIFY))
3280 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3281 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3283 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3284 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3287 /* Reset the variable for hiding menu */
3288 if( menu ) menu->bTimeToHide = FALSE;
3291 /* The return value is only used by TrackPopupMenu */
3292 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3293 if (executedMenuId == -1) executedMenuId = 0;
3294 return executedMenuId;
3297 /***********************************************************************
3298 * MENU_InitTracking
3300 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3302 POPUPMENU *menu;
3304 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3306 HideCaret(0);
3308 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3310 /* This makes the menus of applications built with Delphi work.
3311 * It also enables menus to be displayed in more than one window,
3312 * but there are some bugs left that need to be fixed in this case.
3314 if (!bPopup) menu->hWnd = hWnd;
3315 if (!top_popup)
3317 top_popup = menu->hWnd;
3318 top_popup_hmenu = hMenu;
3321 fEndMenu = FALSE;
3323 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3324 if (!(wFlags & TPM_NONOTIFY))
3325 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3327 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3329 if (!(wFlags & TPM_NONOTIFY))
3331 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3332 /* If an app changed/recreated menu bar entries in WM_INITMENU
3333 * menu sizes will be recalculated once the menu created/shown.
3337 return TRUE;
3340 /***********************************************************************
3341 * MENU_ExitTracking
3343 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3345 TRACE("hwnd=%p\n", hWnd);
3347 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3348 ShowCaret(0);
3349 top_popup = 0;
3350 top_popup_hmenu = NULL;
3351 return TRUE;
3354 /***********************************************************************
3355 * MENU_TrackMouseMenuBar
3357 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3359 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3361 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3362 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3364 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3366 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3367 if (IsMenu(hMenu))
3369 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3371 /* fetch the window menu again, it may have changed */
3372 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3373 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3374 MENU_ExitTracking(hWnd, FALSE);
3379 /***********************************************************************
3380 * MENU_TrackKbdMenuBar
3382 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3384 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3386 UINT uItem = NO_SELECTED_ITEM;
3387 HMENU hTrackMenu;
3388 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3390 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3392 /* find window that has a menu */
3394 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3395 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3397 /* check if we have to track a system menu */
3399 hTrackMenu = GetMenu( hwnd );
3400 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3402 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3403 hTrackMenu = get_win_sys_menu( hwnd );
3404 uItem = 0;
3405 wParam |= HTSYSMENU; /* prevent item lookup */
3407 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3409 if (!IsMenu( hTrackMenu )) return;
3411 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3413 /* fetch the window menu again, it may have changed */
3414 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3416 if( wChar && wChar != ' ' )
3418 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3419 if ( uItem >= (UINT)(-2) )
3421 if( uItem == (UINT)(-1) ) MessageBeep(0);
3422 /* schedule end of menu tracking */
3423 wFlags |= TF_ENDMENU;
3424 goto track_menu;
3428 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3430 if (!(wParam & HTSYSMENU) || wChar == ' ')
3432 if( uItem == NO_SELECTED_ITEM )
3433 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3434 else
3435 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3438 track_menu:
3439 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3440 MENU_ExitTracking( hwnd, FALSE );
3443 /**********************************************************************
3444 * TrackPopupMenuEx (USER32.@)
3446 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3447 HWND hWnd, LPTPMPARAMS lpTpm )
3449 POPUPMENU *menu;
3450 BOOL ret = FALSE;
3452 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3453 hMenu, wFlags, x, y, hWnd, lpTpm,
3454 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3456 /* Parameter check */
3457 /* FIXME: this check is performed several times, here and in the called
3458 functions. That could be optimized */
3459 if (!(menu = MENU_GetMenu( hMenu )))
3461 SetLastError( ERROR_INVALID_MENU_HANDLE );
3462 return FALSE;
3465 if (IsWindow(menu->hWnd))
3467 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3468 return FALSE;
3471 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3473 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3475 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3476 if (!(wFlags & TPM_NONOTIFY))
3477 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3479 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3480 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3481 lpTpm ? &lpTpm->rcExclude : NULL );
3482 MENU_ExitTracking(hWnd, TRUE);
3484 if (menu->hWnd)
3486 DestroyWindow( menu->hWnd );
3487 menu->hWnd = 0;
3489 if (!(wFlags & TPM_NONOTIFY))
3490 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3491 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3495 return ret;
3498 /**********************************************************************
3499 * TrackPopupMenu (USER32.@)
3501 * Like the win32 API, the function return the command ID only if the
3502 * flag TPM_RETURNCMD is on.
3505 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3506 INT nReserved, HWND hWnd, const RECT *lpRect )
3508 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3511 /***********************************************************************
3512 * PopupMenuWndProc
3514 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3516 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3518 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3520 switch(message)
3522 case WM_CREATE:
3524 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3525 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3526 return 0;
3529 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3530 return MA_NOACTIVATE;
3532 case WM_PAINT:
3534 PAINTSTRUCT ps;
3535 BeginPaint( hwnd, &ps );
3536 MENU_DrawPopupMenu( hwnd, ps.hdc,
3537 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3538 EndPaint( hwnd, &ps );
3539 return 0;
3542 case WM_PRINTCLIENT:
3544 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3545 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3546 return 0;
3549 case WM_ERASEBKGND:
3550 return 1;
3552 case WM_DESTROY:
3553 /* zero out global pointer in case resident popup window was destroyed. */
3554 if (hwnd == top_popup) {
3555 top_popup = 0;
3556 top_popup_hmenu = NULL;
3558 break;
3560 case WM_SHOWWINDOW:
3562 if( wParam )
3564 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3566 else
3567 SetWindowLongPtrW( hwnd, 0, 0 );
3568 break;
3570 case MN_GETHMENU:
3571 return GetWindowLongPtrW( hwnd, 0 );
3573 default:
3574 return DefWindowProcW( hwnd, message, wParam, lParam );
3576 return 0;
3580 /***********************************************************************
3581 * MENU_GetMenuBarHeight
3583 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3585 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3586 INT orgX, INT orgY )
3588 HDC hdc;
3589 RECT rectBar;
3590 LPPOPUPMENU lppop;
3592 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3594 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3596 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3597 SelectObject( hdc, get_menu_font(FALSE));
3598 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3599 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3600 ReleaseDC( hwnd, hdc );
3601 return lppop->Height;
3605 /*******************************************************************
3606 * ChangeMenuA (USER32.@)
3608 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3609 UINT id, UINT flags )
3611 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3612 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3613 id, data );
3614 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3615 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3616 id, data );
3617 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3618 flags & MF_BYPOSITION ? pos : id,
3619 flags & ~MF_REMOVE );
3620 /* Default: MF_INSERT */
3621 return InsertMenuA( hMenu, pos, flags, id, data );
3625 /*******************************************************************
3626 * ChangeMenuW (USER32.@)
3628 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3629 UINT id, UINT flags )
3631 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3632 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3633 id, data );
3634 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3635 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3636 id, data );
3637 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3638 flags & MF_BYPOSITION ? pos : id,
3639 flags & ~MF_REMOVE );
3640 /* Default: MF_INSERT */
3641 return InsertMenuW( hMenu, pos, flags, id, data );
3645 /*******************************************************************
3646 * CheckMenuItem (USER32.@)
3648 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3650 MENUITEM *item;
3651 DWORD ret;
3653 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3654 ret = item->fState & MF_CHECKED;
3655 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3656 else item->fState &= ~MF_CHECKED;
3657 return ret;
3661 /**********************************************************************
3662 * EnableMenuItem (USER32.@)
3664 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3666 UINT oldflags;
3667 MENUITEM *item;
3668 POPUPMENU *menu;
3670 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3672 /* Get the Popupmenu to access the owner menu */
3673 if (!(menu = MENU_GetMenu(hMenu)))
3674 return (UINT)-1;
3676 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3677 return (UINT)-1;
3679 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3680 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3682 /* If the close item in the system menu change update the close button */
3683 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3685 if (menu->hSysMenuOwner != 0)
3687 RECT rc;
3688 POPUPMENU* parentMenu;
3690 /* Get the parent menu to access*/
3691 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3692 return (UINT)-1;
3694 /* Refresh the frame to reflect the change */
3695 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3696 rc.bottom = 0;
3697 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3701 return oldflags;
3705 /*******************************************************************
3706 * GetMenuStringA (USER32.@)
3708 INT WINAPI GetMenuStringA(
3709 HMENU hMenu, /* [in] menuhandle */
3710 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3711 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3712 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3713 UINT wFlags /* [in] MF_ flags */
3715 MENUITEM *item;
3717 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3718 if (str && nMaxSiz) str[0] = '\0';
3719 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3720 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3721 return 0;
3723 if (!item->text) return 0;
3724 if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3725 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3726 str[nMaxSiz-1] = 0;
3727 TRACE("returning %s\n", debugstr_a(str));
3728 return strlen(str);
3732 /*******************************************************************
3733 * GetMenuStringW (USER32.@)
3735 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3736 LPWSTR str, INT nMaxSiz, UINT wFlags )
3738 MENUITEM *item;
3740 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3741 if (str && nMaxSiz) str[0] = '\0';
3742 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3743 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3744 return 0;
3746 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3747 if( !(item->text)) {
3748 str[0] = 0;
3749 return 0;
3751 lstrcpynW( str, item->text, nMaxSiz );
3752 TRACE("returning %s\n", debugstr_w(str));
3753 return strlenW(str);
3757 /**********************************************************************
3758 * HiliteMenuItem (USER32.@)
3760 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3761 UINT wHilite )
3763 LPPOPUPMENU menu;
3764 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3765 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3766 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3767 if (menu->FocusedItem == wItemID) return TRUE;
3768 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3769 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3770 return TRUE;
3774 /**********************************************************************
3775 * GetMenuState (USER32.@)
3777 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3779 MENUITEM *item;
3780 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3781 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3782 debug_print_menuitem (" item: ", item, "");
3783 if (item->fType & MF_POPUP)
3785 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3786 if (!menu) return -1;
3787 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3789 else
3791 /* We used to (from way back then) mask the result to 0xff. */
3792 /* I don't know why and it seems wrong as the documented */
3793 /* return flag MF_SEPARATOR is outside that mask. */
3794 return (item->fType | item->fState);
3799 /**********************************************************************
3800 * GetMenuItemCount (USER32.@)
3802 INT WINAPI GetMenuItemCount( HMENU hMenu )
3804 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3805 if (!menu) return -1;
3806 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3807 return menu->nItems;
3811 /**********************************************************************
3812 * GetMenuItemID (USER32.@)
3814 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3816 MENUITEM * lpmi;
3818 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3819 if (lpmi->fType & MF_POPUP) return -1;
3820 return lpmi->wID;
3825 /**********************************************************************
3826 * MENU_mnu2mnuii
3828 * Uses flags, id and text ptr, passed by InsertMenu() and
3829 * ModifyMenu() to setup a MenuItemInfo structure.
3831 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3832 LPMENUITEMINFOW pmii)
3834 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3835 pmii->cbSize = sizeof( MENUITEMINFOW);
3836 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3837 /* setting bitmap clears text and vice versa */
3838 if( IS_STRING_ITEM(flags)) {
3839 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3840 if( !str)
3841 flags |= MF_SEPARATOR;
3842 /* Item beginning with a backspace is a help item */
3843 /* FIXME: wrong place, this is only true in win16 */
3844 else if( *str == '\b') {
3845 flags |= MF_HELP;
3846 str++;
3848 pmii->dwTypeData = (LPWSTR)str;
3849 } else if( flags & MFT_BITMAP){
3850 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3851 pmii->hbmpItem = (HBITMAP)str;
3853 if( flags & MF_OWNERDRAW){
3854 pmii->fMask |= MIIM_DATA;
3855 pmii->dwItemData = (ULONG_PTR) str;
3857 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3858 pmii->fMask |= MIIM_SUBMENU;
3859 pmii->hSubMenu = (HMENU)id;
3861 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3862 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3863 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3864 pmii->wID = (UINT)id;
3868 /*******************************************************************
3869 * InsertMenuW (USER32.@)
3871 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3872 UINT_PTR id, LPCWSTR str )
3874 MENUITEM *item;
3875 MENUITEMINFOW mii;
3877 if (IS_STRING_ITEM(flags) && str)
3878 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3879 hMenu, pos, flags, id, debugstr_w(str) );
3880 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3881 hMenu, pos, flags, id, str );
3883 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3884 MENU_mnu2mnuii( flags, id, str, &mii);
3885 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3887 RemoveMenu( hMenu, pos, flags );
3888 return FALSE;
3891 item->hCheckBit = item->hUnCheckBit = 0;
3892 return TRUE;
3896 /*******************************************************************
3897 * InsertMenuA (USER32.@)
3899 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3900 UINT_PTR id, LPCSTR str )
3902 BOOL ret = FALSE;
3904 if (IS_STRING_ITEM(flags) && str)
3906 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3907 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3908 if (newstr)
3910 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3911 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3912 HeapFree( GetProcessHeap(), 0, newstr );
3914 return ret;
3916 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3920 /*******************************************************************
3921 * AppendMenuA (USER32.@)
3923 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3924 UINT_PTR id, LPCSTR data )
3926 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3930 /*******************************************************************
3931 * AppendMenuW (USER32.@)
3933 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3934 UINT_PTR id, LPCWSTR data )
3936 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3940 /**********************************************************************
3941 * RemoveMenu (USER32.@)
3943 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3945 LPPOPUPMENU menu;
3946 MENUITEM *item;
3948 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3949 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3950 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3952 /* Remove item */
3954 MENU_FreeItemData( item );
3956 if (--menu->nItems == 0)
3958 HeapFree( GetProcessHeap(), 0, menu->items );
3959 menu->items = NULL;
3961 else
3963 while(nPos < menu->nItems)
3965 *item = *(item+1);
3966 item++;
3967 nPos++;
3969 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3970 menu->nItems * sizeof(MENUITEM) );
3972 return TRUE;
3976 /**********************************************************************
3977 * DeleteMenu (USER32.@)
3979 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3981 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3982 if (!item) return FALSE;
3983 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3984 /* nPos is now the position of the item */
3985 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3986 return TRUE;
3990 /*******************************************************************
3991 * ModifyMenuW (USER32.@)
3993 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3994 UINT_PTR id, LPCWSTR str )
3996 MENUITEM *item;
3997 MENUITEMINFOW mii;
3999 if (IS_STRING_ITEM(flags))
4000 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
4001 else
4002 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
4004 if (!(item = MENU_FindItem( &hMenu, &pos, flags )))
4006 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4007 if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
4008 return FALSE;
4010 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4011 MENU_mnu2mnuii( flags, id, str, &mii);
4012 return SetMenuItemInfo_common( item, &mii, TRUE);
4016 /*******************************************************************
4017 * ModifyMenuA (USER32.@)
4019 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4020 UINT_PTR id, LPCSTR str )
4022 BOOL ret = FALSE;
4024 if (IS_STRING_ITEM(flags) && str)
4026 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4027 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4028 if (newstr)
4030 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4031 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4032 HeapFree( GetProcessHeap(), 0, newstr );
4034 return ret;
4036 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4040 /**********************************************************************
4041 * CreatePopupMenu (USER32.@)
4043 HMENU WINAPI CreatePopupMenu(void)
4045 HMENU hmenu;
4046 POPUPMENU *menu;
4048 if (!(hmenu = CreateMenu())) return 0;
4049 menu = MENU_GetMenu( hmenu );
4050 menu->wFlags |= MF_POPUP;
4051 menu->bTimeToHide = FALSE;
4052 return hmenu;
4056 /**********************************************************************
4057 * GetMenuCheckMarkDimensions (USER.417)
4058 * GetMenuCheckMarkDimensions (USER32.@)
4060 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4062 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4066 /**********************************************************************
4067 * SetMenuItemBitmaps (USER32.@)
4069 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4070 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4072 MENUITEM *item;
4074 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4076 if (!hNewCheck && !hNewUnCheck)
4078 item->fState &= ~MF_USECHECKBITMAPS;
4080 else /* Install new bitmaps */
4082 item->hCheckBit = hNewCheck;
4083 item->hUnCheckBit = hNewUnCheck;
4084 item->fState |= MF_USECHECKBITMAPS;
4086 return TRUE;
4090 /**********************************************************************
4091 * CreateMenu (USER32.@)
4093 HMENU WINAPI CreateMenu(void)
4095 HMENU hMenu;
4096 LPPOPUPMENU menu;
4098 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4099 menu->FocusedItem = NO_SELECTED_ITEM;
4100 menu->bTimeToHide = FALSE;
4102 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4104 TRACE("return %p\n", hMenu );
4106 return hMenu;
4110 /**********************************************************************
4111 * DestroyMenu (USER32.@)
4113 BOOL WINAPI DestroyMenu( HMENU hMenu )
4115 LPPOPUPMENU lppop;
4117 TRACE("(%p)\n", hMenu);
4119 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4120 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4122 /* DestroyMenu should not destroy system menu popup owner */
4123 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4125 DestroyWindow( lppop->hWnd );
4126 lppop->hWnd = 0;
4129 if (lppop->items) /* recursively destroy submenus */
4131 int i;
4132 MENUITEM *item = lppop->items;
4133 for (i = lppop->nItems; i > 0; i--, item++)
4135 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4136 MENU_FreeItemData( item );
4138 HeapFree( GetProcessHeap(), 0, lppop->items );
4140 HeapFree( GetProcessHeap(), 0, lppop );
4141 return TRUE;
4145 /**********************************************************************
4146 * GetSystemMenu (USER32.@)
4148 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4150 WND *wndPtr = WIN_GetPtr( hWnd );
4151 HMENU retvalue = 0;
4153 if (wndPtr == WND_DESKTOP) return 0;
4154 if (wndPtr == WND_OTHER_PROCESS)
4156 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4158 else if (wndPtr)
4160 if (wndPtr->hSysMenu && bRevert)
4162 DestroyMenu(wndPtr->hSysMenu);
4163 wndPtr->hSysMenu = 0;
4166 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4167 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4169 if( wndPtr->hSysMenu )
4171 POPUPMENU *menu;
4172 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4174 /* Store the dummy sysmenu handle to facilitate the refresh */
4175 /* of the close button if the SC_CLOSE item change */
4176 menu = MENU_GetMenu(retvalue);
4177 if ( menu )
4178 menu->hSysMenuOwner = wndPtr->hSysMenu;
4180 WIN_ReleasePtr( wndPtr );
4182 return bRevert ? 0 : retvalue;
4186 /*******************************************************************
4187 * SetSystemMenu (USER32.@)
4189 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4191 WND *wndPtr = WIN_GetPtr( hwnd );
4193 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4195 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4196 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4197 WIN_ReleasePtr( wndPtr );
4198 return TRUE;
4200 return FALSE;
4204 /**********************************************************************
4205 * GetMenu (USER32.@)
4207 HMENU WINAPI GetMenu( HWND hWnd )
4209 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4210 TRACE("for %p returning %p\n", hWnd, retvalue);
4211 return retvalue;
4214 /**********************************************************************
4215 * GetMenuBarInfo (USER32.@)
4217 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4219 POPUPMENU *menu;
4220 HMENU hmenu = NULL;
4221 ATOM class_atom;
4223 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4225 switch (idObject)
4227 case OBJID_CLIENT:
4228 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4229 if (!class_atom)
4230 return FALSE;
4231 if (class_atom != POPUPMENU_CLASS_ATOM)
4233 WARN("called on invalid window: %d\n", class_atom);
4234 SetLastError(ERROR_INVALID_MENU_HANDLE);
4235 return FALSE;
4238 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4239 break;
4240 case OBJID_MENU:
4241 hmenu = GetMenu(hwnd);
4242 break;
4243 case OBJID_SYSMENU:
4244 hmenu = GetSystemMenu(hwnd, FALSE);
4245 break;
4246 default:
4247 return FALSE;
4250 if (!hmenu)
4251 return FALSE;
4253 if (pmbi->cbSize != sizeof(MENUBARINFO))
4255 SetLastError(ERROR_INVALID_PARAMETER);
4256 return FALSE;
4259 menu = MENU_GetMenu(hmenu);
4260 if (!menu)
4261 return FALSE;
4262 if (idItem < 0 || idItem > menu->nItems)
4263 return FALSE;
4265 if (!menu->Height)
4267 SetRectEmpty(&pmbi->rcBar);
4269 else if (idItem == 0)
4271 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4272 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4273 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4275 else
4277 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4280 pmbi->hMenu = hmenu;
4281 pmbi->hwndMenu = NULL;
4282 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4283 if (idItem)
4285 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4286 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4288 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4289 if (menu)
4290 pmbi->hwndMenu = menu->hWnd;
4293 else
4295 pmbi->fFocused = pmbi->fBarFocused;
4298 return TRUE;
4301 /**********************************************************************
4302 * MENU_SetMenu
4304 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4305 * SetWindowPos call that would result if SetMenu were called directly.
4307 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4309 TRACE("(%p, %p);\n", hWnd, hMenu);
4311 if (hMenu && !IsMenu(hMenu))
4313 WARN("hMenu %p is not a menu handle\n", hMenu);
4314 return FALSE;
4316 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4317 return FALSE;
4319 hWnd = WIN_GetFullHandle( hWnd );
4320 if (GetCapture() == hWnd)
4321 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4323 if (hMenu != 0)
4325 LPPOPUPMENU lpmenu;
4327 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4329 lpmenu->hWnd = hWnd;
4330 lpmenu->Height = 0; /* Make sure we recalculate the size */
4332 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4333 return TRUE;
4337 /**********************************************************************
4338 * SetMenu (USER32.@)
4340 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4342 if(!MENU_SetMenu(hWnd, hMenu))
4343 return FALSE;
4345 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4346 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4347 return TRUE;
4351 /**********************************************************************
4352 * GetSubMenu (USER32.@)
4354 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4356 MENUITEM * lpmi;
4358 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4359 if (!(lpmi->fType & MF_POPUP)) return 0;
4360 return lpmi->hSubMenu;
4364 /**********************************************************************
4365 * DrawMenuBar (USER32.@)
4367 BOOL WINAPI DrawMenuBar( HWND hWnd )
4369 LPPOPUPMENU lppop;
4370 HMENU hMenu;
4372 if (!IsWindow( hWnd ))
4373 return FALSE;
4374 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4375 return TRUE;
4377 if ((hMenu = GetMenu( hWnd )) && (lppop = MENU_GetMenu( hMenu ))) {
4378 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4379 lppop->hwndOwner = hWnd;
4382 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4383 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4386 /***********************************************************************
4387 * DrawMenuBarTemp (USER32.@)
4389 * UNDOCUMENTED !!
4391 * called by W98SE desk.cpl Control Panel Applet
4393 * Not 100% sure about the param names, but close.
4395 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4397 LPPOPUPMENU lppop;
4398 UINT i,retvalue;
4399 HFONT hfontOld = 0;
4400 BOOL flat_menu = FALSE;
4402 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4404 if (!hMenu)
4405 hMenu = GetMenu(hwnd);
4407 if (!hFont)
4408 hFont = get_menu_font(FALSE);
4410 lppop = MENU_GetMenu( hMenu );
4411 if (lppop == NULL || lprect == NULL)
4413 retvalue = GetSystemMetrics(SM_CYMENU);
4414 goto END;
4417 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4419 hfontOld = SelectObject( hDC, hFont);
4421 if (lppop->Height == 0)
4422 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4424 lprect->bottom = lprect->top + lppop->Height;
4426 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4428 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4429 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4430 LineTo( hDC, lprect->right, lprect->bottom );
4432 if (lppop->nItems == 0)
4434 retvalue = GetSystemMetrics(SM_CYMENU);
4435 goto END;
4438 for (i = 0; i < lppop->nItems; i++)
4440 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4441 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4443 retvalue = lppop->Height;
4445 END:
4446 if (hfontOld) SelectObject (hDC, hfontOld);
4447 return retvalue;
4450 /***********************************************************************
4451 * EndMenu (USER.187)
4452 * EndMenu (USER32.@)
4454 BOOL WINAPI EndMenu(void)
4456 /* if we are in the menu code, and it is active */
4457 if (!fEndMenu && top_popup)
4459 /* terminate the menu handling code */
4460 fEndMenu = TRUE;
4462 /* needs to be posted to wakeup the internal menu handler */
4463 /* which will now terminate the menu, in the event that */
4464 /* the main window was minimized, or lost focus, so we */
4465 /* don't end up with an orphaned menu */
4466 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4468 return fEndMenu;
4472 /*****************************************************************
4473 * LoadMenuA (USER32.@)
4475 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4477 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4478 if (!hrsrc) return 0;
4479 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4483 /*****************************************************************
4484 * LoadMenuW (USER32.@)
4486 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4488 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4489 if (!hrsrc) return 0;
4490 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4494 /**********************************************************************
4495 * LoadMenuIndirectW (USER32.@)
4497 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4499 HMENU hMenu;
4500 WORD version, offset;
4501 LPCSTR p = template;
4503 version = GET_WORD(p);
4504 p += sizeof(WORD);
4505 TRACE("%p, ver %d\n", template, version );
4506 switch (version)
4508 case 0: /* standard format is version of 0 */
4509 offset = GET_WORD(p);
4510 p += sizeof(WORD) + offset;
4511 if (!(hMenu = CreateMenu())) return 0;
4512 if (!MENU_ParseResource( p, hMenu ))
4514 DestroyMenu( hMenu );
4515 return 0;
4517 return hMenu;
4518 case 1: /* extended format is version of 1 */
4519 offset = GET_WORD(p);
4520 p += sizeof(WORD) + offset;
4521 if (!(hMenu = CreateMenu())) return 0;
4522 if (!MENUEX_ParseResource( p, hMenu))
4524 DestroyMenu( hMenu );
4525 return 0;
4527 return hMenu;
4528 default:
4529 ERR("version %d not supported.\n", version);
4530 return 0;
4535 /**********************************************************************
4536 * LoadMenuIndirectA (USER32.@)
4538 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4540 return LoadMenuIndirectW( template );
4544 /**********************************************************************
4545 * IsMenu (USER32.@)
4547 BOOL WINAPI IsMenu(HMENU hmenu)
4549 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4551 if (!menu)
4553 SetLastError(ERROR_INVALID_MENU_HANDLE);
4554 return FALSE;
4556 return TRUE;
4559 /**********************************************************************
4560 * GetMenuItemInfo_common
4563 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4564 LPMENUITEMINFOW lpmii, BOOL unicode)
4566 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4568 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4570 if (!menu) {
4571 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4572 return FALSE;
4575 if( lpmii->fMask & MIIM_TYPE) {
4576 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4577 WARN("invalid combination of fMask bits used\n");
4578 /* this does not happen on Win9x/ME */
4579 SetLastError( ERROR_INVALID_PARAMETER);
4580 return FALSE;
4582 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4583 if (menu->hbmpItem && !IS_MAGIC_BITMAP(menu->hbmpItem))
4584 lpmii->fType |= MFT_BITMAP;
4585 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4586 if( lpmii->fType & MFT_BITMAP) {
4587 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4588 lpmii->cch = 0;
4589 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4590 /* this does not happen on Win9x/ME */
4591 lpmii->dwTypeData = 0;
4592 lpmii->cch = 0;
4596 /* copy the text string */
4597 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4598 if( !menu->text ) {
4599 if(lpmii->dwTypeData && lpmii->cch) {
4600 if( unicode)
4601 *((WCHAR *)lpmii->dwTypeData) = 0;
4602 else
4603 *((CHAR *)lpmii->dwTypeData) = 0;
4605 lpmii->cch = 0;
4606 } else {
4607 int len;
4608 if (unicode)
4610 len = strlenW(menu->text);
4611 if(lpmii->dwTypeData && lpmii->cch)
4612 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4614 else
4616 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4617 0, NULL, NULL ) - 1;
4618 if(lpmii->dwTypeData && lpmii->cch)
4619 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4620 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4621 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4623 /* if we've copied a substring we return its length */
4624 if(lpmii->dwTypeData && lpmii->cch)
4625 if (lpmii->cch <= len + 1)
4626 lpmii->cch--;
4627 else
4628 lpmii->cch = len;
4629 else {
4630 /* return length of string */
4631 /* not on Win9x/ME if fType & MFT_BITMAP */
4632 lpmii->cch = len;
4637 if (lpmii->fMask & MIIM_FTYPE)
4638 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4640 if (lpmii->fMask & MIIM_BITMAP)
4641 lpmii->hbmpItem = menu->hbmpItem;
4643 if (lpmii->fMask & MIIM_STATE)
4644 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4646 if (lpmii->fMask & MIIM_ID)
4647 lpmii->wID = menu->wID;
4649 if (lpmii->fMask & MIIM_SUBMENU)
4650 lpmii->hSubMenu = menu->hSubMenu;
4651 else {
4652 /* hSubMenu is always cleared
4653 * (not on Win9x/ME ) */
4654 lpmii->hSubMenu = 0;
4657 if (lpmii->fMask & MIIM_CHECKMARKS) {
4658 lpmii->hbmpChecked = menu->hCheckBit;
4659 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4661 if (lpmii->fMask & MIIM_DATA)
4662 lpmii->dwItemData = menu->dwItemData;
4664 return TRUE;
4667 /**********************************************************************
4668 * GetMenuItemInfoA (USER32.@)
4670 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4671 LPMENUITEMINFOA lpmii)
4673 BOOL ret;
4674 MENUITEMINFOA mii;
4675 if( lpmii->cbSize != sizeof( mii) &&
4676 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4677 SetLastError( ERROR_INVALID_PARAMETER);
4678 return FALSE;
4680 memcpy( &mii, lpmii, lpmii->cbSize);
4681 mii.cbSize = sizeof( mii);
4682 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4683 (LPMENUITEMINFOW)&mii, FALSE);
4684 mii.cbSize = lpmii->cbSize;
4685 memcpy( lpmii, &mii, mii.cbSize);
4686 return ret;
4689 /**********************************************************************
4690 * GetMenuItemInfoW (USER32.@)
4692 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4693 LPMENUITEMINFOW lpmii)
4695 BOOL ret;
4696 MENUITEMINFOW mii;
4697 if( lpmii->cbSize != sizeof( mii) &&
4698 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4699 SetLastError( ERROR_INVALID_PARAMETER);
4700 return FALSE;
4702 memcpy( &mii, lpmii, lpmii->cbSize);
4703 mii.cbSize = sizeof( mii);
4704 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4705 mii.cbSize = lpmii->cbSize;
4706 memcpy( lpmii, &mii, mii.cbSize);
4707 return ret;
4711 /* set a menu item text from a ASCII or Unicode string */
4712 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4714 if (!text)
4715 menu->text = NULL;
4716 else if (unicode)
4718 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4719 strcpyW( menu->text, text );
4721 else
4723 LPCSTR str = (LPCSTR)text;
4724 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4725 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4726 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4731 /**********************************************************************
4732 * MENU_depth
4734 * detect if there are loops in the menu tree (or the depth is too large)
4736 static int MENU_depth( POPUPMENU *pmenu, int depth)
4738 UINT i;
4739 MENUITEM *item;
4740 int subdepth;
4742 depth++;
4743 if( depth > MAXMENUDEPTH) return depth;
4744 item = pmenu->items;
4745 subdepth = depth;
4746 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4747 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4748 if( psubmenu){
4749 int bdepth = MENU_depth( psubmenu, depth);
4750 if( bdepth > subdepth) subdepth = bdepth;
4752 if( subdepth > MAXMENUDEPTH)
4753 TRACE("<- hmenu %p\n", item->hSubMenu);
4755 return subdepth;
4759 /**********************************************************************
4760 * SetMenuItemInfo_common
4762 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4763 * MIIM_BITMAP and MIIM_STRING flags instead.
4766 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4767 const MENUITEMINFOW *lpmii,
4768 BOOL unicode)
4770 if (!menu) return FALSE;
4772 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4774 if (lpmii->fMask & MIIM_FTYPE ) {
4775 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4776 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4778 if (lpmii->fMask & MIIM_STRING ) {
4779 /* free the string when used */
4780 HeapFree(GetProcessHeap(), 0, menu->text);
4781 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4784 if (lpmii->fMask & MIIM_STATE)
4785 /* Other menu items having MFS_DEFAULT are not converted
4786 to normal items */
4787 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4789 if (lpmii->fMask & MIIM_ID)
4790 menu->wID = lpmii->wID;
4792 if (lpmii->fMask & MIIM_SUBMENU) {
4793 menu->hSubMenu = lpmii->hSubMenu;
4794 if (menu->hSubMenu) {
4795 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4796 if (subMenu) {
4797 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4798 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4799 menu->hSubMenu = 0;
4800 return FALSE;
4802 subMenu->wFlags |= MF_POPUP;
4803 menu->fType |= MF_POPUP;
4804 } else {
4805 SetLastError( ERROR_INVALID_PARAMETER);
4806 return FALSE;
4809 else
4810 menu->fType &= ~MF_POPUP;
4813 if (lpmii->fMask & MIIM_CHECKMARKS)
4815 menu->hCheckBit = lpmii->hbmpChecked;
4816 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4818 if (lpmii->fMask & MIIM_DATA)
4819 menu->dwItemData = lpmii->dwItemData;
4821 if (lpmii->fMask & MIIM_BITMAP)
4822 menu->hbmpItem = lpmii->hbmpItem;
4824 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4825 menu->fType |= MFT_SEPARATOR;
4827 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4828 return TRUE;
4831 /**********************************************************************
4832 * MENU_NormalizeMenuItemInfoStruct
4834 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4835 * check, copy and extend the MENUITEMINFO struct from the version that the application
4836 * supplied to the version used by wine source. */
4837 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4838 MENUITEMINFOW *pmii_out )
4840 /* do we recognize the size? */
4841 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4842 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4843 SetLastError( ERROR_INVALID_PARAMETER);
4844 return FALSE;
4846 /* copy the fields that we have */
4847 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4848 /* if the hbmpItem member is missing then extend */
4849 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4850 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4851 pmii_out->hbmpItem = NULL;
4853 /* test for invalid bit combinations */
4854 if( (pmii_out->fMask & MIIM_TYPE &&
4855 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4856 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4857 WARN("invalid combination of fMask bits used\n");
4858 /* this does not happen on Win9x/ME */
4859 SetLastError( ERROR_INVALID_PARAMETER);
4860 return FALSE;
4862 /* convert old style (MIIM_TYPE) to the new */
4863 if( pmii_out->fMask & MIIM_TYPE){
4864 pmii_out->fMask |= MIIM_FTYPE;
4865 if( IS_STRING_ITEM(pmii_out->fType)){
4866 pmii_out->fMask |= MIIM_STRING;
4867 } else if( (pmii_out->fType) & MFT_BITMAP){
4868 pmii_out->fMask |= MIIM_BITMAP;
4869 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4872 return TRUE;
4875 /**********************************************************************
4876 * SetMenuItemInfoA (USER32.@)
4878 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4879 const MENUITEMINFOA *lpmii)
4881 MENUITEM *menuitem;
4882 MENUITEMINFOW mii;
4884 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4886 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4888 if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
4890 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4891 if (item == SC_TASKLIST && !bypos) return TRUE;
4892 return FALSE;
4894 return SetMenuItemInfo_common( menuitem, &mii, FALSE );
4897 /**********************************************************************
4898 * SetMenuItemInfoW (USER32.@)
4900 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4901 const MENUITEMINFOW *lpmii)
4903 MENUITEM *menuitem;
4904 MENUITEMINFOW mii;
4906 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4908 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4909 if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
4911 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4912 if (item == SC_TASKLIST && !bypos) return TRUE;
4913 return FALSE;
4915 return SetMenuItemInfo_common( menuitem, &mii, TRUE );
4918 /**********************************************************************
4919 * SetMenuDefaultItem (USER32.@)
4922 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4924 UINT i;
4925 POPUPMENU *menu;
4926 MENUITEM *item;
4928 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4930 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4932 /* reset all default-item flags */
4933 item = menu->items;
4934 for (i = 0; i < menu->nItems; i++, item++)
4936 item->fState &= ~MFS_DEFAULT;
4939 /* no default item */
4940 if ( -1 == uItem)
4942 return TRUE;
4945 item = menu->items;
4946 if ( bypos )
4948 if ( uItem >= menu->nItems ) return FALSE;
4949 item[uItem].fState |= MFS_DEFAULT;
4950 return TRUE;
4952 else
4954 for (i = 0; i < menu->nItems; i++, item++)
4956 if (item->wID == uItem)
4958 item->fState |= MFS_DEFAULT;
4959 return TRUE;
4964 return FALSE;
4967 /**********************************************************************
4968 * GetMenuDefaultItem (USER32.@)
4970 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4972 POPUPMENU *menu;
4973 MENUITEM * item;
4974 UINT i = 0;
4976 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4978 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4980 /* find default item */
4981 item = menu->items;
4983 /* empty menu */
4984 if (! item) return -1;
4986 while ( !( item->fState & MFS_DEFAULT ) )
4988 i++; item++;
4989 if (i >= menu->nItems ) return -1;
4992 /* default: don't return disabled items */
4993 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4995 /* search rekursiv when needed */
4996 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4998 UINT ret;
4999 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
5000 if ( -1 != ret ) return ret;
5002 /* when item not found in submenu, return the popup item */
5004 return ( bypos ) ? i : item->wID;
5009 /**********************************************************************
5010 * InsertMenuItemA (USER32.@)
5012 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
5013 const MENUITEMINFOA *lpmii)
5015 MENUITEM *item;
5016 MENUITEMINFOW mii;
5018 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5020 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5022 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5023 return SetMenuItemInfo_common(item, &mii, FALSE);
5027 /**********************************************************************
5028 * InsertMenuItemW (USER32.@)
5030 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5031 const MENUITEMINFOW *lpmii)
5033 MENUITEM *item;
5034 MENUITEMINFOW mii;
5036 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5038 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5040 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5041 return SetMenuItemInfo_common(item, &mii, TRUE);
5044 /**********************************************************************
5045 * CheckMenuRadioItem (USER32.@)
5048 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5049 UINT first, UINT last, UINT check,
5050 UINT bypos)
5052 BOOL done = FALSE;
5053 UINT i;
5054 MENUITEM *mi_first = NULL, *mi_check;
5055 HMENU m_first, m_check;
5057 for (i = first; i <= last; i++)
5059 UINT pos = i;
5061 if (!mi_first)
5063 m_first = hMenu;
5064 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5065 if (!mi_first) continue;
5066 mi_check = mi_first;
5067 m_check = m_first;
5069 else
5071 m_check = hMenu;
5072 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5073 if (!mi_check) continue;
5076 if (m_first != m_check) continue;
5077 if (mi_check->fType == MFT_SEPARATOR) continue;
5079 if (i == check)
5081 mi_check->fType |= MFT_RADIOCHECK;
5082 mi_check->fState |= MFS_CHECKED;
5083 done = TRUE;
5085 else
5087 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5088 mi_check->fState &= ~MFS_CHECKED;
5092 return done;
5096 /**********************************************************************
5097 * GetMenuItemRect (USER32.@)
5099 * ATTENTION: Here, the returned values in rect are the screen
5100 * coordinates of the item just like if the menu was
5101 * always on the upper left side of the application.
5104 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5105 LPRECT rect)
5107 POPUPMENU *itemMenu;
5108 MENUITEM *item;
5109 HWND referenceHwnd;
5111 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5113 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5114 referenceHwnd = hwnd;
5116 if(!hwnd)
5118 itemMenu = MENU_GetMenu(hMenu);
5119 if (itemMenu == NULL)
5120 return FALSE;
5122 if(itemMenu->hWnd == 0)
5123 return FALSE;
5124 referenceHwnd = itemMenu->hWnd;
5127 if ((rect == NULL) || (item == NULL))
5128 return FALSE;
5130 *rect = item->rect;
5132 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5134 return TRUE;
5137 /**********************************************************************
5138 * SetMenuInfo (USER32.@)
5140 * FIXME
5141 * actually use the items to draw the menu
5142 * (recalculate and/or redraw)
5144 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5146 POPUPMENU *menu;
5147 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5149 if (lpmi->fMask & MIM_BACKGROUND)
5150 menu->hbrBack = lpmi->hbrBack;
5152 if (lpmi->fMask & MIM_HELPID)
5153 menu->dwContextHelpID = lpmi->dwContextHelpID;
5155 if (lpmi->fMask & MIM_MAXHEIGHT)
5156 menu->cyMax = lpmi->cyMax;
5158 if (lpmi->fMask & MIM_MENUDATA)
5159 menu->dwMenuData = lpmi->dwMenuData;
5161 if (lpmi->fMask & MIM_STYLE)
5162 menu->dwStyle = lpmi->dwStyle;
5164 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5165 int i;
5166 MENUITEM *item = menu->items;
5167 for( i = menu->nItems; i; i--, item++)
5168 if( item->fType & MF_POPUP)
5169 menu_SetMenuInfo( item->hSubMenu, lpmi);
5171 return TRUE;
5174 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5176 TRACE("(%p %p)\n", hMenu, lpmi);
5177 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5178 if( lpmi->fMask & MIM_STYLE) {
5179 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5180 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5181 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5183 return TRUE;
5185 SetLastError( ERROR_INVALID_PARAMETER);
5186 return FALSE;
5189 /**********************************************************************
5190 * GetMenuInfo (USER32.@)
5192 * NOTES
5193 * win98/NT5.0
5196 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5197 { POPUPMENU *menu;
5199 TRACE("(%p %p)\n", hMenu, lpmi);
5201 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5204 if (lpmi->fMask & MIM_BACKGROUND)
5205 lpmi->hbrBack = menu->hbrBack;
5207 if (lpmi->fMask & MIM_HELPID)
5208 lpmi->dwContextHelpID = menu->dwContextHelpID;
5210 if (lpmi->fMask & MIM_MAXHEIGHT)
5211 lpmi->cyMax = menu->cyMax;
5213 if (lpmi->fMask & MIM_MENUDATA)
5214 lpmi->dwMenuData = menu->dwMenuData;
5216 if (lpmi->fMask & MIM_STYLE)
5217 lpmi->dwStyle = menu->dwStyle;
5219 return TRUE;
5221 SetLastError( ERROR_INVALID_PARAMETER);
5222 return FALSE;
5226 /**********************************************************************
5227 * SetMenuContextHelpId (USER32.@)
5229 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5231 LPPOPUPMENU menu;
5233 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5235 if ((menu = MENU_GetMenu(hMenu)))
5237 menu->dwContextHelpID = dwContextHelpID;
5238 return TRUE;
5240 return FALSE;
5244 /**********************************************************************
5245 * GetMenuContextHelpId (USER32.@)
5247 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5249 LPPOPUPMENU menu;
5251 TRACE("(%p)\n", hMenu);
5253 if ((menu = MENU_GetMenu(hMenu)))
5255 return menu->dwContextHelpID;
5257 return 0;
5260 /**********************************************************************
5261 * MenuItemFromPoint (USER32.@)
5263 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5265 POPUPMENU *menu = MENU_GetMenu(hMenu);
5266 UINT pos;
5268 /*FIXME: Do we have to handle hWnd here? */
5269 if (!menu) return -1;
5270 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5271 return pos;
5275 /**********************************************************************
5276 * translate_accelerator
5278 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5279 BYTE fVirt, WORD key, WORD cmd )
5281 INT mask = 0;
5282 UINT mesg = 0;
5284 if (wParam != key) return FALSE;
5286 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5287 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5288 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5290 if (message == WM_CHAR || message == WM_SYSCHAR)
5292 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5294 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5295 goto found;
5298 else
5300 if(fVirt & FVIRTKEY)
5302 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5303 wParam, 0xff & HIWORD(lParam));
5305 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5306 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5308 else
5310 if (!(lParam & 0x01000000)) /* no special_key */
5312 if ((fVirt & FALT) && (lParam & 0x20000000))
5313 { /* ^^ ALT pressed */
5314 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5315 goto found;
5320 return FALSE;
5322 found:
5323 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5324 mesg = 1;
5325 else
5327 HMENU hMenu, hSubMenu, hSysMenu;
5328 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5330 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5331 hSysMenu = get_win_sys_menu( hWnd );
5333 /* find menu item and ask application to initialize it */
5334 /* 1. in the system menu */
5335 hSubMenu = hSysMenu;
5336 nPos = cmd;
5337 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5339 if (GetCapture())
5340 mesg = 2;
5341 if (!IsWindowEnabled(hWnd))
5342 mesg = 3;
5343 else
5345 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5346 if(hSubMenu != hSysMenu)
5348 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5349 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5350 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5352 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5355 else /* 2. in the window's menu */
5357 hSubMenu = hMenu;
5358 nPos = cmd;
5359 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5361 if (GetCapture())
5362 mesg = 2;
5363 if (!IsWindowEnabled(hWnd))
5364 mesg = 3;
5365 else
5367 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5368 if(hSubMenu != hMenu)
5370 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5371 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5372 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5374 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5379 if (mesg == 0)
5381 if (uSysStat != (UINT)-1)
5383 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5384 mesg=4;
5385 else
5386 mesg=WM_SYSCOMMAND;
5388 else
5390 if (uStat != (UINT)-1)
5392 if (IsIconic(hWnd))
5393 mesg=5;
5394 else
5396 if (uStat & (MF_DISABLED|MF_GRAYED))
5397 mesg=6;
5398 else
5399 mesg=WM_COMMAND;
5402 else
5403 mesg=WM_COMMAND;
5408 if( mesg==WM_COMMAND )
5410 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5411 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5413 else if( mesg==WM_SYSCOMMAND )
5415 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5416 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5418 else
5420 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5421 * #0: unknown (please report!)
5422 * #1: for WM_KEYUP,WM_SYSKEYUP
5423 * #2: mouse is captured
5424 * #3: window is disabled
5425 * #4: it's a disabled system menu option
5426 * #5: it's a menu option, but window is iconic
5427 * #6: it's a menu option, but disabled
5429 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5430 if(mesg==0)
5431 ERR_(accel)(" unknown reason - please report!\n");
5433 return TRUE;
5436 /**********************************************************************
5437 * TranslateAcceleratorA (USER32.@)
5438 * TranslateAccelerator (USER32.@)
5440 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5442 switch (msg->message)
5444 case WM_KEYDOWN:
5445 case WM_SYSKEYDOWN:
5446 return TranslateAcceleratorW( hWnd, hAccel, msg );
5448 case WM_CHAR:
5449 case WM_SYSCHAR:
5451 MSG msgW = *msg;
5452 char ch = LOWORD(msg->wParam);
5453 WCHAR wch;
5454 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5455 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5456 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5459 default:
5460 return 0;
5464 /**********************************************************************
5465 * TranslateAcceleratorW (USER32.@)
5467 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5469 ACCEL data[32], *ptr = data;
5470 int i, count;
5472 if (!hWnd) return 0;
5474 if (msg->message != WM_KEYDOWN &&
5475 msg->message != WM_SYSKEYDOWN &&
5476 msg->message != WM_CHAR &&
5477 msg->message != WM_SYSCHAR)
5478 return 0;
5480 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5481 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5483 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5484 if (count > sizeof(data)/sizeof(data[0]))
5486 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5488 count = CopyAcceleratorTableW( hAccel, ptr, count );
5489 for (i = 0; i < count; i++)
5491 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5492 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5493 break;
5495 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5496 return (i < count);