msvcirt: Add implementation of streambuf::sputbackc.
[wine.git] / dlls / user32 / menu.c
blob847540b63afca41affbb47a8be886a8e336433a2
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;
1462 dis.CtlType = ODT_MENU;
1463 dis.CtlID = 0;
1464 dis.itemID = lpitem->wID;
1465 dis.itemData = lpitem->dwItemData;
1466 dis.itemState = 0;
1467 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1468 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1469 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1470 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1471 dis.hwndItem = (HWND)hmenu;
1472 dis.hDC = hdc;
1473 dis.rcItem = rect;
1474 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1475 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1476 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1477 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1478 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1479 /* Draw the popup-menu arrow */
1480 if (lpitem->fType & MF_POPUP)
1481 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1482 arrow_bitmap_height);
1483 return;
1486 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1488 if (lpitem->fState & MF_HILITE)
1490 if (flat_menu)
1492 InflateRect (&rect, -1, -1);
1493 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1494 InflateRect (&rect, 1, 1);
1495 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1497 else
1499 if(menuBar)
1500 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1501 else
1502 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1505 else
1506 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1508 SetBkMode( hdc, TRANSPARENT );
1510 /* vertical separator */
1511 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1513 HPEN oldPen;
1514 RECT rc = rect;
1516 rc.left -= MENU_COL_SPACE / 2 + 1;
1517 rc.top = 3;
1518 rc.bottom = height - 3;
1519 if (flat_menu)
1521 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1522 MoveToEx( hdc, rc.left, rc.top, NULL );
1523 LineTo( hdc, rc.left, rc.bottom );
1524 SelectObject( hdc, oldPen );
1526 else
1527 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1530 /* horizontal separator */
1531 if (lpitem->fType & MF_SEPARATOR)
1533 HPEN oldPen;
1534 RECT rc = rect;
1536 rc.left++;
1537 rc.right--;
1538 rc.top = ( rc.top + rc.bottom) / 2;
1539 if (flat_menu)
1541 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1542 MoveToEx( hdc, rc.left, rc.top, NULL );
1543 LineTo( hdc, rc.right, rc.top );
1544 SelectObject( hdc, oldPen );
1546 else
1547 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1548 return;
1551 /* helper lines for debugging */
1552 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1553 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1554 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1555 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1558 if (lpitem->hbmpItem) {
1559 /* calculate the bitmap rectangle in coordinates relative
1560 * to the item rectangle */
1561 if( menuBar) {
1562 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1563 bmprc.left = 3;
1564 else
1565 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1567 else if (menu->dwStyle & MNS_NOCHECK)
1568 bmprc.left = 4;
1569 else if (menu->dwStyle & MNS_CHECKORBMP)
1570 bmprc.left = 2;
1571 else
1572 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1573 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1574 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1575 bmprc.top = 0;
1576 else
1577 bmprc.top = (rect.bottom - rect.top -
1578 lpitem->bmpsize.cy) / 2;
1579 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1582 if (!menuBar)
1584 HBITMAP bm;
1585 INT y = rect.top + rect.bottom;
1586 RECT rc = rect;
1587 BOOL checked = FALSE;
1588 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1589 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1590 /* Draw the check mark
1592 * FIXME:
1593 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1595 if( !(menu->dwStyle & MNS_NOCHECK)) {
1596 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1597 lpitem->hUnCheckBit;
1598 if (bm) /* we have a custom bitmap */
1600 HDC hdcMem = CreateCompatibleDC( hdc );
1602 SelectObject( hdcMem, bm );
1603 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1604 check_bitmap_width, check_bitmap_height,
1605 hdcMem, 0, 0, SRCCOPY );
1606 DeleteDC( hdcMem );
1607 checked = TRUE;
1609 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1611 RECT r;
1612 HBITMAP bm = CreateBitmap( check_bitmap_width,
1613 check_bitmap_height, 1, 1, NULL );
1614 HDC hdcMem = CreateCompatibleDC( hdc );
1616 SelectObject( hdcMem, bm );
1617 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1618 DrawFrameControl( hdcMem, &r, DFC_MENU,
1619 (lpitem->fType & MFT_RADIOCHECK) ?
1620 DFCS_MENUBULLET : DFCS_MENUCHECK );
1621 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1622 hdcMem, 0, 0, SRCCOPY );
1623 DeleteDC( hdcMem );
1624 DeleteObject( bm );
1625 checked = TRUE;
1628 if( lpitem->hbmpItem &&
1629 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1630 POINT origorg;
1631 /* some applications make this assumption on the DC's origin */
1632 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1633 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1634 odaction, FALSE);
1635 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1637 /* Draw the popup-menu arrow */
1638 if (lpitem->fType & MF_POPUP)
1639 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1640 arrow_bitmap_height);
1641 rect.left += 4;
1642 if( !(menu->dwStyle & MNS_NOCHECK))
1643 rect.left += check_bitmap_width;
1644 rect.right -= arrow_bitmap_width;
1646 else if( lpitem->hbmpItem)
1647 { /* Draw the bitmap */
1648 POINT origorg;
1650 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1651 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1652 odaction, menuBar);
1653 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1655 /* process text if present */
1656 if (lpitem->text)
1658 int i;
1659 HFONT hfontOld = 0;
1661 UINT uFormat = (menuBar) ?
1662 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1663 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1665 if( !(menu->dwStyle & MNS_CHECKORBMP))
1666 rect.left += menu->textOffset;
1668 if ( lpitem->fState & MFS_DEFAULT )
1670 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1673 if (menuBar) {
1674 if( lpitem->hbmpItem)
1675 rect.left += lpitem->bmpsize.cx;
1676 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1677 rect.left += menucharsize.cx;
1678 rect.right -= menucharsize.cx;
1681 for (i = 0; lpitem->text[i]; i++)
1682 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1683 break;
1685 if(lpitem->fState & MF_GRAYED)
1687 if (!(lpitem->fState & MF_HILITE) )
1689 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1690 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1691 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1692 --rect.left; --rect.top; --rect.right; --rect.bottom;
1694 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1697 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1699 /* paint the shortcut text */
1700 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1702 if (lpitem->text[i] == '\t')
1704 rect.left = lpitem->xTab;
1705 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1707 else
1709 rect.right = lpitem->xTab;
1710 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1713 if(lpitem->fState & MF_GRAYED)
1715 if (!(lpitem->fState & MF_HILITE) )
1717 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1718 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1719 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1720 --rect.left; --rect.top; --rect.right; --rect.bottom;
1722 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1724 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1727 if (hfontOld)
1728 SelectObject (hdc, hfontOld);
1733 /***********************************************************************
1734 * MENU_DrawPopupMenu
1736 * Paint a popup menu.
1738 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1740 HBRUSH hPrevBrush = 0, brush = GetSysColorBrush( COLOR_MENU );
1741 RECT rect;
1742 POPUPMENU *menu = MENU_GetMenu( hmenu );
1744 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1746 GetClientRect( hwnd, &rect );
1748 if (menu && menu->hbrBack) brush = menu->hbrBack;
1749 if ((hPrevBrush = SelectObject( hdc, brush ))
1750 && SelectObject( hdc, get_menu_font(FALSE) ))
1752 HPEN hPrevPen;
1754 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1756 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1757 if( hPrevPen )
1759 BOOL flat_menu = FALSE;
1761 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1762 if (flat_menu)
1763 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1764 else
1765 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1767 if (menu)
1769 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1770 /* draw menu items */
1771 if( menu->nItems)
1773 MENUITEM *item;
1774 UINT u;
1776 item = menu->items;
1777 for( u = menu->nItems; u > 0; u--, item++)
1778 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1779 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1781 /* draw scroll arrows */
1782 if (menu->bScrolling)
1783 MENU_DrawScrollArrows(menu, hdc);
1785 } else
1787 SelectObject( hdc, hPrevBrush );
1792 /***********************************************************************
1793 * MENU_DrawMenuBar
1795 * Paint a menu bar. Returns the height of the menu bar.
1796 * called from [windows/nonclient.c]
1798 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1800 LPPOPUPMENU lppop;
1801 HMENU hMenu = GetMenu(hwnd);
1803 lppop = MENU_GetMenu( hMenu );
1804 if (lppop == NULL || lprect == NULL)
1806 return GetSystemMetrics(SM_CYMENU);
1809 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1813 /***********************************************************************
1814 * MENU_InitPopup
1816 * Popup menu initialization before WM_ENTERMENULOOP.
1818 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1820 POPUPMENU *menu;
1821 DWORD ex_style = 0;
1823 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1825 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1827 /* store the owner for DrawItem */
1828 if (!IsWindow( hwndOwner ))
1830 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1831 return FALSE;
1833 menu->hwndOwner = hwndOwner;
1835 if (flags & TPM_LAYOUTRTL)
1836 ex_style = WS_EX_LAYOUTRTL;
1838 /* NOTE: In Windows, top menu popup is not owned. */
1839 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1840 WS_POPUP, 0, 0, 0, 0,
1841 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1842 (LPVOID)hmenu );
1843 if( !menu->hWnd ) return FALSE;
1844 return TRUE;
1848 /***********************************************************************
1849 * MENU_ShowPopup
1851 * Display a popup menu.
1853 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1854 INT x, INT y, INT xanchor, INT yanchor )
1856 POPUPMENU *menu;
1857 INT width, height;
1858 POINT pt;
1859 HMONITOR monitor;
1860 MONITORINFO info;
1862 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1863 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1865 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1866 if (menu->FocusedItem != NO_SELECTED_ITEM)
1868 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1869 menu->FocusedItem = NO_SELECTED_ITEM;
1872 menu->nScrollPos = 0;
1873 MENU_PopupMenuCalcSize( menu );
1875 /* adjust popup menu pos so that it fits within the desktop */
1877 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1878 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1880 /* FIXME: should use item rect */
1881 pt.x = x;
1882 pt.y = y;
1883 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1884 info.cbSize = sizeof(info);
1885 GetMonitorInfoW( monitor, &info );
1887 if (flags & TPM_LAYOUTRTL)
1888 flags ^= TPM_RIGHTALIGN;
1890 if( flags & TPM_RIGHTALIGN ) x -= width;
1891 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1893 if( flags & TPM_BOTTOMALIGN ) y -= height;
1894 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1896 if( x + width > info.rcWork.right)
1898 if( xanchor && x >= width - xanchor )
1899 x -= width - xanchor;
1901 if( x + width > info.rcWork.right)
1902 x = info.rcWork.right - width;
1904 if( x < info.rcWork.left ) x = info.rcWork.left;
1906 if( y + height > info.rcWork.bottom)
1908 if( yanchor && y >= height + yanchor )
1909 y -= height + yanchor;
1911 if( y + height > info.rcWork.bottom)
1912 y = info.rcWork.bottom - height;
1914 if( y < info.rcWork.top ) y = info.rcWork.top;
1916 if (!top_popup) {
1917 top_popup = menu->hWnd;
1918 top_popup_hmenu = hmenu;
1920 /* Display the window */
1922 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, width, height,
1923 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1924 UpdateWindow( menu->hWnd );
1925 return TRUE;
1929 /***********************************************************************
1930 * MENU_EnsureMenuItemVisible
1932 static void
1933 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1935 if (lppop->bScrolling)
1937 MENUITEM *item = &lppop->items[wIndex];
1938 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1939 UINT nOldPos = lppop->nScrollPos;
1940 RECT rc;
1941 UINT arrow_bitmap_height;
1942 BITMAP bmp;
1944 GetClientRect(lppop->hWnd, &rc);
1946 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1947 arrow_bitmap_height = bmp.bmHeight;
1949 rc.top += arrow_bitmap_height;
1950 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1952 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1953 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1956 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1957 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1958 MENU_DrawScrollArrows(lppop, hdc);
1960 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1962 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1963 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1964 MENU_DrawScrollArrows(lppop, hdc);
1970 /***********************************************************************
1971 * MENU_SelectItem
1973 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1974 BOOL sendMenuSelect, HMENU topmenu )
1976 LPPOPUPMENU lppop;
1977 HDC hdc;
1979 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1981 lppop = MENU_GetMenu( hmenu );
1982 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1984 if (lppop->FocusedItem == wIndex) return;
1985 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1986 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1987 if (!top_popup) {
1988 top_popup = lppop->hWnd;
1989 top_popup_hmenu = hmenu;
1992 SelectObject( hdc, get_menu_font(FALSE));
1994 /* Clear previous highlighted item */
1995 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1997 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1998 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1999 lppop->Height, !(lppop->wFlags & MF_POPUP),
2000 ODA_SELECT );
2003 /* Highlight new item (if any) */
2004 lppop->FocusedItem = wIndex;
2005 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2007 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2008 lppop->items[wIndex].fState |= MF_HILITE;
2009 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2010 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2011 &lppop->items[wIndex], lppop->Height,
2012 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2014 if (sendMenuSelect)
2016 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2017 SendMessageW( hwndOwner, WM_MENUSELECT,
2018 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2019 ip->fType | ip->fState |
2020 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2023 else if (sendMenuSelect) {
2024 if(topmenu){
2025 int pos;
2026 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2027 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2028 MENUITEM *ip = &ptm->items[pos];
2029 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2030 ip->fType | ip->fState |
2031 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2035 ReleaseDC( lppop->hWnd, hdc );
2039 /***********************************************************************
2040 * MENU_MoveSelection
2042 * Moves currently selected item according to the offset parameter.
2043 * If there is no selection then it should select the last item if
2044 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2046 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2048 INT i;
2049 POPUPMENU *menu;
2051 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2053 menu = MENU_GetMenu( hmenu );
2054 if ((!menu) || (!menu->items)) return;
2056 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2058 if( menu->nItems == 1 ) return; else
2059 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2060 ; i += offset)
2061 if (!(menu->items[i].fType & MF_SEPARATOR))
2063 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2064 return;
2068 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2069 i >= 0 && i < menu->nItems ; i += offset)
2070 if (!(menu->items[i].fType & MF_SEPARATOR))
2072 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2073 return;
2078 /**********************************************************************
2079 * MENU_InsertItem
2081 * Insert (allocate) a new item into a menu.
2083 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2085 MENUITEM *newItems;
2086 POPUPMENU *menu;
2088 if (!(menu = MENU_GetMenu(hMenu)))
2089 return NULL;
2091 /* Find where to insert new item */
2093 if (flags & MF_BYPOSITION) {
2094 if (pos > menu->nItems)
2095 pos = menu->nItems;
2096 } else {
2097 if (!MENU_FindItem( &hMenu, &pos, flags ))
2098 pos = menu->nItems;
2099 else {
2100 if (!(menu = MENU_GetMenu( hMenu )))
2101 return NULL;
2105 /* Make sure that MDI system buttons stay on the right side.
2106 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2107 * regardless of their id.
2109 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2110 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2111 pos--;
2113 TRACE("inserting at %u flags %x\n", pos, flags);
2115 /* Create new items array */
2117 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2118 if (!newItems)
2120 WARN("allocation failed\n" );
2121 return NULL;
2123 if (menu->nItems > 0)
2125 /* Copy the old array into the new one */
2126 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2127 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2128 (menu->nItems-pos)*sizeof(MENUITEM) );
2129 HeapFree( GetProcessHeap(), 0, menu->items );
2131 menu->items = newItems;
2132 menu->nItems++;
2133 memset( &newItems[pos], 0, sizeof(*newItems) );
2134 menu->Height = 0; /* force size recalculate */
2135 return &newItems[pos];
2139 /**********************************************************************
2140 * MENU_ParseResource
2142 * Parse a standard menu resource and add items to the menu.
2143 * Return a pointer to the end of the resource.
2145 * NOTE: flags is equivalent to the mtOption field
2147 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2149 WORD flags, id = 0;
2150 LPCWSTR str;
2151 BOOL end_flag;
2155 flags = GET_WORD(res);
2156 end_flag = flags & MF_END;
2157 /* Remove MF_END because it has the same value as MF_HILITE */
2158 flags &= ~MF_END;
2159 res += sizeof(WORD);
2160 if (!(flags & MF_POPUP))
2162 id = GET_WORD(res);
2163 res += sizeof(WORD);
2165 str = (LPCWSTR)res;
2166 res += (strlenW(str) + 1) * sizeof(WCHAR);
2167 if (flags & MF_POPUP)
2169 HMENU hSubMenu = CreatePopupMenu();
2170 if (!hSubMenu) return NULL;
2171 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2172 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2174 else /* Not a popup */
2176 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2178 } while (!end_flag);
2179 return res;
2183 /**********************************************************************
2184 * MENUEX_ParseResource
2186 * Parse an extended menu resource and add items to the menu.
2187 * Return a pointer to the end of the resource.
2189 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2191 WORD resinfo;
2192 do {
2193 MENUITEMINFOW mii;
2195 mii.cbSize = sizeof(mii);
2196 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2197 mii.fType = GET_DWORD(res);
2198 res += sizeof(DWORD);
2199 mii.fState = GET_DWORD(res);
2200 res += sizeof(DWORD);
2201 mii.wID = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2204 res += sizeof(WORD);
2205 /* Align the text on a word boundary. */
2206 res += (~((UINT_PTR)res - 1)) & 1;
2207 mii.dwTypeData = (LPWSTR) res;
2208 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2209 /* Align the following fields on a dword boundary. */
2210 res += (~((UINT_PTR)res - 1)) & 3;
2212 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2213 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2215 if (resinfo & 1) { /* Pop-up? */
2216 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2217 res += sizeof(DWORD);
2218 mii.hSubMenu = CreatePopupMenu();
2219 if (!mii.hSubMenu)
2220 return NULL;
2221 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2222 DestroyMenu(mii.hSubMenu);
2223 return NULL;
2225 mii.fMask |= MIIM_SUBMENU;
2226 mii.fType |= MF_POPUP;
2228 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2230 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2231 mii.wID, mii.fType);
2232 mii.fType |= MF_SEPARATOR;
2234 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2235 } while (!(resinfo & MF_END));
2236 return res;
2240 /***********************************************************************
2241 * MENU_GetSubPopup
2243 * Return the handle of the selected sub-popup menu (if any).
2245 static HMENU MENU_GetSubPopup( HMENU hmenu )
2247 POPUPMENU *menu;
2248 MENUITEM *item;
2250 menu = MENU_GetMenu( hmenu );
2252 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2254 item = &menu->items[menu->FocusedItem];
2255 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2256 return item->hSubMenu;
2257 return 0;
2261 /***********************************************************************
2262 * MENU_HideSubPopups
2264 * Hide the sub-popup menus of this menu.
2266 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2267 BOOL sendMenuSelect, UINT wFlags )
2269 POPUPMENU *menu = MENU_GetMenu( hmenu );
2271 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2273 if (menu && top_popup)
2275 HMENU hsubmenu;
2276 POPUPMENU *submenu;
2277 MENUITEM *item;
2279 if (menu->FocusedItem != NO_SELECTED_ITEM)
2281 item = &menu->items[menu->FocusedItem];
2282 if (!(item->fType & MF_POPUP) ||
2283 !(item->fState & MF_MOUSESELECT)) return;
2284 item->fState &= ~MF_MOUSESELECT;
2285 hsubmenu = item->hSubMenu;
2286 } else return;
2288 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2289 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2290 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2291 DestroyWindow( submenu->hWnd );
2292 submenu->hWnd = 0;
2294 if (!(wFlags & TPM_NONOTIFY))
2295 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2296 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2301 /***********************************************************************
2302 * MENU_ShowSubPopup
2304 * Display the sub-menu of the selected item of this menu.
2305 * Return the handle of the submenu, or hmenu if no submenu to display.
2307 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2308 BOOL selectFirst, UINT wFlags )
2310 RECT rect;
2311 POPUPMENU *menu;
2312 MENUITEM *item;
2313 HDC hdc;
2315 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2317 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2319 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2321 item = &menu->items[menu->FocusedItem];
2322 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2323 return hmenu;
2325 /* message must be sent before using item,
2326 because nearly everything may be changed by the application ! */
2328 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2329 if (!(wFlags & TPM_NONOTIFY))
2330 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2331 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2333 item = &menu->items[menu->FocusedItem];
2334 rect = item->rect;
2336 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2337 if (!(item->fState & MF_HILITE))
2339 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2340 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2342 SelectObject( hdc, get_menu_font(FALSE));
2344 item->fState |= MF_HILITE;
2345 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2346 ReleaseDC( menu->hWnd, hdc );
2348 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2349 item->rect = rect;
2351 item->fState |= MF_MOUSESELECT;
2353 if (IS_SYSTEM_MENU(menu))
2355 MENU_InitSysMenuPopup(item->hSubMenu,
2356 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2357 GetClassLongW( menu->hWnd, GCL_STYLE));
2359 NC_GetSysPopupPos( menu->hWnd, &rect );
2360 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2361 rect.top = rect.bottom;
2362 rect.right = GetSystemMetrics(SM_CXSIZE);
2363 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2365 else
2367 GetWindowRect( menu->hWnd, &rect );
2368 if (menu->wFlags & MF_POPUP)
2370 RECT rc = item->rect;
2372 MENU_AdjustMenuItemRect(menu, &rc);
2374 /* The first item in the popup menu has to be at the
2375 same y position as the focused menu item */
2376 if (wFlags & TPM_LAYOUTRTL)
2377 rect.left += GetSystemMetrics(SM_CXBORDER);
2378 else
2379 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2380 rect.top += rc.top - MENU_TOP_MARGIN;
2381 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2382 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2383 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2385 else
2387 if (wFlags & TPM_LAYOUTRTL)
2388 rect.left = rect.right - item->rect.left;
2389 else
2390 rect.left += item->rect.left;
2391 rect.top += item->rect.bottom;
2392 rect.right = item->rect.right - item->rect.left;
2393 rect.bottom = item->rect.bottom - item->rect.top;
2397 /* use default alignment for submenus */
2398 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2400 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2402 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2403 rect.left, rect.top, rect.right, rect.bottom );
2404 if (selectFirst)
2405 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2406 return item->hSubMenu;
2411 /**********************************************************************
2412 * MENU_IsMenuActive
2414 HWND MENU_IsMenuActive(void)
2416 return top_popup;
2419 /**********************************************************************
2420 * MENU_EndMenu
2422 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2424 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2426 void MENU_EndMenu( HWND hwnd )
2428 POPUPMENU *menu;
2429 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2430 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2433 /***********************************************************************
2434 * MENU_PtMenu
2436 * Walks menu chain trying to find a menu pt maps to.
2438 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2440 POPUPMENU *menu = MENU_GetMenu( hMenu );
2441 UINT item = menu->FocusedItem;
2442 HMENU ret;
2444 /* try subpopup first (if any) */
2445 ret = (item != NO_SELECTED_ITEM &&
2446 (menu->items[item].fType & MF_POPUP) &&
2447 (menu->items[item].fState & MF_MOUSESELECT))
2448 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2450 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2452 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2453 if( menu->wFlags & MF_POPUP )
2455 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2457 else if (ht == HTSYSMENU)
2458 ret = get_win_sys_menu( menu->hWnd );
2459 else if (ht == HTMENU)
2460 ret = GetMenu( menu->hWnd );
2462 return ret;
2465 /***********************************************************************
2466 * MENU_ExecFocusedItem
2468 * Execute a menu item (for instance when user pressed Enter).
2469 * Return the wID of the executed item. Otherwise, -1 indicating
2470 * that no menu item was executed, -2 if a popup is shown;
2471 * Have to receive the flags for the TrackPopupMenu options to avoid
2472 * sending unwanted message.
2475 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2477 MENUITEM *item;
2478 POPUPMENU *menu = MENU_GetMenu( hMenu );
2480 TRACE("%p hmenu=%p\n", pmt, hMenu);
2482 if (!menu || !menu->nItems ||
2483 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2485 item = &menu->items[menu->FocusedItem];
2487 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2489 if (!(item->fType & MF_POPUP))
2491 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2493 /* If TPM_RETURNCMD is set you return the id, but
2494 do not send a message to the owner */
2495 if(!(wFlags & TPM_RETURNCMD))
2497 if( menu->wFlags & MF_SYSMENU )
2498 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2499 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2500 else
2502 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2503 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2505 if (dwStyle & MNS_NOTIFYBYPOS)
2506 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2507 (LPARAM)hMenu);
2508 else
2509 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2512 return item->wID;
2515 else
2517 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2518 return -2;
2521 return -1;
2524 /***********************************************************************
2525 * MENU_SwitchTracking
2527 * Helper function for menu navigation routines.
2529 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2531 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2532 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2534 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2536 if( pmt->hTopMenu != hPtMenu &&
2537 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2539 /* both are top level menus (system and menu-bar) */
2540 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2541 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2542 pmt->hTopMenu = hPtMenu;
2544 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2545 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2549 /***********************************************************************
2550 * MENU_ButtonDown
2552 * Return TRUE if we can go on with menu tracking.
2554 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2556 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2558 if (hPtMenu)
2560 UINT id = 0;
2561 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2562 MENUITEM *item;
2564 if( IS_SYSTEM_MENU(ptmenu) )
2565 item = ptmenu->items;
2566 else
2567 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2569 if( item )
2571 if( ptmenu->FocusedItem != id )
2572 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2574 /* If the popup menu is not already "popped" */
2575 if(!(item->fState & MF_MOUSESELECT ))
2577 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2580 return TRUE;
2582 /* Else the click was on the menu bar, finish the tracking */
2584 return FALSE;
2587 /***********************************************************************
2588 * MENU_ButtonUp
2590 * Return the value of MENU_ExecFocusedItem if
2591 * the selected item was not a popup. Else open the popup.
2592 * A -1 return value indicates that we go on with menu tracking.
2595 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2597 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2599 if (hPtMenu)
2601 UINT id = 0;
2602 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2603 MENUITEM *item;
2605 if( IS_SYSTEM_MENU(ptmenu) )
2606 item = ptmenu->items;
2607 else
2608 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2610 if( item && (ptmenu->FocusedItem == id ))
2612 debug_print_menuitem ("FocusedItem: ", item, "");
2614 if( !(item->fType & MF_POPUP) )
2616 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2617 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2618 return executedMenuId;
2621 /* If we are dealing with the menu bar */
2622 /* and this is a click on an already "popped" item: */
2623 /* Stop the menu tracking and close the opened submenus */
2624 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2625 return 0;
2627 if( GetMenu(ptmenu->hWnd) == hPtMenu )
2628 ptmenu->bTimeToHide = TRUE;
2630 return -1;
2634 /***********************************************************************
2635 * MENU_MouseMove
2637 * Return TRUE if we can go on with menu tracking.
2639 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2641 UINT id = NO_SELECTED_ITEM;
2642 POPUPMENU *ptmenu = NULL;
2644 if( hPtMenu )
2646 ptmenu = MENU_GetMenu( hPtMenu );
2647 if( IS_SYSTEM_MENU(ptmenu) )
2648 id = 0;
2649 else
2650 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2653 if( id == NO_SELECTED_ITEM )
2655 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2656 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2659 else if( ptmenu->FocusedItem != id )
2661 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2662 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2664 return TRUE;
2668 /***********************************************************************
2669 * MENU_DoNextMenu
2671 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2673 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2675 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2676 BOOL atEnd = FALSE;
2678 /* When skipping left, we need to do something special after the
2679 first menu. */
2680 if (vk == VK_LEFT && menu->FocusedItem == 0)
2682 atEnd = TRUE;
2684 /* When skipping right, for the non-system menu, we need to
2685 handle the last non-special menu item (ie skip any window
2686 icons such as MDI maximize, restore or close) */
2687 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2689 UINT i = menu->FocusedItem + 1;
2690 while (i < menu->nItems) {
2691 if ((menu->items[i].wID >= SC_SIZE &&
2692 menu->items[i].wID <= SC_RESTORE)) {
2693 i++;
2694 } else break;
2696 if (i == menu->nItems) {
2697 atEnd = TRUE;
2700 /* When skipping right, we need to cater for the system menu */
2701 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2703 if (menu->FocusedItem == (menu->nItems - 1)) {
2704 atEnd = TRUE;
2708 if( atEnd )
2710 MDINEXTMENU next_menu;
2711 HMENU hNewMenu;
2712 HWND hNewWnd;
2713 UINT id = 0;
2715 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2716 next_menu.hmenuNext = 0;
2717 next_menu.hwndNext = 0;
2718 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2720 TRACE("%p [%p] -> %p [%p]\n",
2721 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2723 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2725 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2726 hNewWnd = pmt->hOwnerWnd;
2727 if( IS_SYSTEM_MENU(menu) )
2729 /* switch to the menu bar */
2731 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2733 if( vk == VK_LEFT )
2735 menu = MENU_GetMenu( hNewMenu );
2736 id = menu->nItems - 1;
2738 /* Skip backwards over any system predefined icons,
2739 eg. MDI close, restore etc icons */
2740 while ((id > 0) &&
2741 (menu->items[id].wID >= SC_SIZE &&
2742 menu->items[id].wID <= SC_RESTORE)) id--;
2745 else if (style & WS_SYSMENU )
2747 /* switch to the system menu */
2748 hNewMenu = get_win_sys_menu( hNewWnd );
2750 else return FALSE;
2752 else /* application returned a new menu to switch to */
2754 hNewMenu = next_menu.hmenuNext;
2755 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2757 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2759 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2761 if (style & WS_SYSMENU &&
2762 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2764 /* get the real system menu */
2765 hNewMenu = get_win_sys_menu(hNewWnd);
2767 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2769 /* FIXME: Not sure what to do here;
2770 * perhaps try to track hNewMenu as a popup? */
2772 TRACE(" -- got confused.\n");
2773 return FALSE;
2776 else return FALSE;
2779 if( hNewMenu != pmt->hTopMenu )
2781 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2782 FALSE, 0 );
2783 if( pmt->hCurrentMenu != pmt->hTopMenu )
2784 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2787 if( hNewWnd != pmt->hOwnerWnd )
2789 pmt->hOwnerWnd = hNewWnd;
2790 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2793 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2794 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2796 return TRUE;
2798 return FALSE;
2801 /***********************************************************************
2802 * MENU_SuspendPopup
2804 * The idea is not to show the popup if the next input message is
2805 * going to hide it anyway.
2807 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2809 MSG msg;
2811 msg.hwnd = pmt->hOwnerWnd;
2813 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2814 pmt->trackFlags |= TF_SKIPREMOVE;
2816 switch( uMsg )
2818 case WM_KEYDOWN:
2819 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2820 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2822 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2823 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2824 if( msg.message == WM_KEYDOWN &&
2825 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2827 pmt->trackFlags |= TF_SUSPENDPOPUP;
2828 return TRUE;
2831 break;
2834 /* failures go through this */
2835 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2836 return FALSE;
2839 /***********************************************************************
2840 * MENU_KeyEscape
2842 * Handle a VK_ESCAPE key event in a menu.
2844 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2846 BOOL bEndMenu = TRUE;
2848 if (pmt->hCurrentMenu != pmt->hTopMenu)
2850 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2852 if (menu->wFlags & MF_POPUP)
2854 HMENU hmenutmp, hmenuprev;
2856 hmenuprev = hmenutmp = pmt->hTopMenu;
2858 /* close topmost popup */
2859 while (hmenutmp != pmt->hCurrentMenu)
2861 hmenuprev = hmenutmp;
2862 hmenutmp = MENU_GetSubPopup( hmenuprev );
2865 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2866 pmt->hCurrentMenu = hmenuprev;
2867 bEndMenu = FALSE;
2871 return bEndMenu;
2874 /***********************************************************************
2875 * MENU_KeyLeft
2877 * Handle a VK_LEFT key event in a menu.
2879 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2881 POPUPMENU *menu;
2882 HMENU hmenutmp, hmenuprev;
2883 UINT prevcol;
2885 hmenuprev = hmenutmp = pmt->hTopMenu;
2886 menu = MENU_GetMenu( hmenutmp );
2888 /* Try to move 1 column left (if possible) */
2889 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2890 NO_SELECTED_ITEM ) {
2892 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2893 prevcol, TRUE, 0 );
2894 return;
2897 /* close topmost popup */
2898 while (hmenutmp != pmt->hCurrentMenu)
2900 hmenuprev = hmenutmp;
2901 hmenutmp = MENU_GetSubPopup( hmenuprev );
2904 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2905 pmt->hCurrentMenu = hmenuprev;
2907 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2909 /* move menu bar selection if no more popups are left */
2911 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2912 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2914 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2916 /* A sublevel menu was displayed - display the next one
2917 * unless there is another displacement coming up */
2919 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2920 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2921 pmt->hTopMenu, TRUE, wFlags);
2927 /***********************************************************************
2928 * MENU_KeyRight
2930 * Handle a VK_RIGHT key event in a menu.
2932 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2934 HMENU hmenutmp;
2935 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2936 UINT nextcol;
2938 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2939 pmt->hCurrentMenu,
2940 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2941 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2943 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2945 /* If already displaying a popup, try to display sub-popup */
2947 hmenutmp = pmt->hCurrentMenu;
2948 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2950 /* if subpopup was displayed then we are done */
2951 if (hmenutmp != pmt->hCurrentMenu) return;
2954 /* Check to see if there's another column */
2955 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2956 NO_SELECTED_ITEM ) {
2957 TRACE("Going to %d.\n", nextcol );
2958 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2959 nextcol, TRUE, 0 );
2960 return;
2963 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2965 if( pmt->hCurrentMenu != pmt->hTopMenu )
2967 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2968 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2969 } else hmenutmp = 0;
2971 /* try to move to the next item */
2972 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2973 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2975 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2976 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2977 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2978 pmt->hTopMenu, TRUE, wFlags);
2982 static void CALLBACK release_capture( BOOL __normal )
2984 set_capture_window( 0, GUI_INMENUMODE, NULL );
2987 /***********************************************************************
2988 * MENU_TrackMenu
2990 * Menu tracking code.
2992 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2993 HWND hwnd, const RECT *lprect )
2995 MSG msg;
2996 POPUPMENU *menu;
2997 BOOL fRemove;
2998 INT executedMenuId = -1;
2999 MTRACKER mt;
3000 BOOL enterIdleSent = FALSE;
3001 HWND capture_win;
3003 mt.trackFlags = 0;
3004 mt.hCurrentMenu = hmenu;
3005 mt.hTopMenu = hmenu;
3006 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3007 mt.pt.x = x;
3008 mt.pt.y = y;
3010 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3011 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3013 if (!(menu = MENU_GetMenu( hmenu )))
3015 WARN("Invalid menu handle %p\n", hmenu);
3016 SetLastError(ERROR_INVALID_MENU_HANDLE);
3017 return FALSE;
3020 if (wFlags & TPM_BUTTONDOWN)
3022 /* Get the result in order to start the tracking or not */
3023 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3024 fEndMenu = !fRemove;
3027 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3029 /* owner may not be visible when tracking a popup, so use the menu itself */
3030 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3031 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3033 __TRY while (!fEndMenu)
3035 menu = MENU_GetMenu( mt.hCurrentMenu );
3036 if (!menu) /* sometimes happens if I do a window manager close */
3037 break;
3039 /* we have to keep the message in the queue until it's
3040 * clear that menu loop is not over yet. */
3042 for (;;)
3044 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3046 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3047 /* remove the message from the queue */
3048 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3050 else
3052 if (!enterIdleSent)
3054 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3055 enterIdleSent = TRUE;
3056 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3058 WaitMessage();
3062 /* check if EndMenu() tried to cancel us, by posting this message */
3063 if(msg.message == WM_CANCELMODE)
3065 /* we are now out of the loop */
3066 fEndMenu = TRUE;
3068 /* remove the message from the queue */
3069 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3071 /* break out of internal loop, ala ESCAPE */
3072 break;
3075 TranslateMessage( &msg );
3076 mt.pt = msg.pt;
3078 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3079 enterIdleSent=FALSE;
3081 fRemove = FALSE;
3082 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3085 * Use the mouse coordinates in lParam instead of those in the MSG
3086 * struct to properly handle synthetic messages. They are already
3087 * in screen coordinates.
3089 mt.pt.x = (short)LOWORD(msg.lParam);
3090 mt.pt.y = (short)HIWORD(msg.lParam);
3092 /* Find a menu for this mouse event */
3093 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3095 switch(msg.message)
3097 /* no WM_NC... messages in captured state */
3099 case WM_RBUTTONDBLCLK:
3100 case WM_RBUTTONDOWN:
3101 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3102 /* fall through */
3103 case WM_LBUTTONDBLCLK:
3104 case WM_LBUTTONDOWN:
3105 /* If the message belongs to the menu, removes it from the queue */
3106 /* Else, end menu tracking */
3107 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3108 fEndMenu = !fRemove;
3109 break;
3111 case WM_RBUTTONUP:
3112 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3113 /* fall through */
3114 case WM_LBUTTONUP:
3115 /* Check if a menu was selected by the mouse */
3116 if (hmenu)
3118 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3119 TRACE("executedMenuId %d\n", executedMenuId);
3121 /* End the loop if executedMenuId is an item ID */
3122 /* or if the job was done (executedMenuId = 0). */
3123 fEndMenu = fRemove = (executedMenuId != -1);
3125 /* No menu was selected by the mouse */
3126 /* if the function was called by TrackPopupMenu, continue
3127 with the menu tracking. If not, stop it */
3128 else
3129 fEndMenu = !(wFlags & TPM_POPUPMENU);
3131 break;
3133 case WM_MOUSEMOVE:
3134 /* the selected menu item must be changed every time */
3135 /* the mouse moves. */
3137 if (hmenu)
3138 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3140 } /* switch(msg.message) - mouse */
3142 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3144 fRemove = TRUE; /* Keyboard messages are always removed */
3145 switch(msg.message)
3147 case WM_KEYDOWN:
3148 case WM_SYSKEYDOWN:
3149 switch(msg.wParam)
3151 case VK_MENU:
3152 case VK_F10:
3153 fEndMenu = TRUE;
3154 break;
3156 case VK_HOME:
3157 case VK_END:
3158 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3159 NO_SELECTED_ITEM, FALSE, 0 );
3160 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3161 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3162 break;
3164 case VK_UP:
3165 case VK_DOWN: /* If on menu bar, pull-down the menu */
3167 menu = MENU_GetMenu( mt.hCurrentMenu );
3168 if (!(menu->wFlags & MF_POPUP))
3169 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3170 else /* otherwise try to move selection */
3171 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3172 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3173 break;
3175 case VK_LEFT:
3176 MENU_KeyLeft( &mt, wFlags );
3177 break;
3179 case VK_RIGHT:
3180 MENU_KeyRight( &mt, wFlags );
3181 break;
3183 case VK_ESCAPE:
3184 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3185 break;
3187 case VK_F1:
3189 HELPINFO hi;
3190 hi.cbSize = sizeof(HELPINFO);
3191 hi.iContextType = HELPINFO_MENUITEM;
3192 if (menu->FocusedItem == NO_SELECTED_ITEM)
3193 hi.iCtrlId = 0;
3194 else
3195 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3196 hi.hItemHandle = hmenu;
3197 hi.dwContextId = menu->dwContextHelpID;
3198 hi.MousePos = msg.pt;
3199 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3200 break;
3203 default:
3204 break;
3206 break; /* WM_KEYDOWN */
3208 case WM_CHAR:
3209 case WM_SYSCHAR:
3211 UINT pos;
3213 if (msg.wParam == '\r' || msg.wParam == ' ')
3215 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3216 fEndMenu = (executedMenuId != -2);
3218 break;
3221 /* Hack to avoid control chars. */
3222 /* We will find a better way real soon... */
3223 if (msg.wParam < 32) break;
3225 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3226 LOWORD(msg.wParam), FALSE );
3227 if (pos == (UINT)-2) fEndMenu = TRUE;
3228 else if (pos == (UINT)-1) MessageBeep(0);
3229 else
3231 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3232 TRUE, 0 );
3233 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3234 fEndMenu = (executedMenuId != -2);
3237 break;
3238 } /* switch(msg.message) - kbd */
3240 else
3242 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3243 DispatchMessageW( &msg );
3244 continue;
3247 if (!fEndMenu) fRemove = TRUE;
3249 /* finally remove message from the queue */
3251 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3252 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3253 else mt.trackFlags &= ~TF_SKIPREMOVE;
3255 __FINALLY( release_capture )
3257 /* If dropdown is still painted and the close box is clicked on
3258 then the menu will be destroyed as part of the DispatchMessage above.
3259 This will then invalidate the menu handle in mt.hTopMenu. We should
3260 check for this first. */
3261 if( IsMenu( mt.hTopMenu ) )
3263 menu = MENU_GetMenu( mt.hTopMenu );
3265 if( IsWindow( mt.hOwnerWnd ) )
3267 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3269 if (menu && (menu->wFlags & MF_POPUP))
3271 DestroyWindow( menu->hWnd );
3272 menu->hWnd = 0;
3274 if (!(wFlags & TPM_NONOTIFY))
3275 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3276 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3278 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3279 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3282 /* Reset the variable for hiding menu */
3283 if( menu ) menu->bTimeToHide = FALSE;
3286 /* The return value is only used by TrackPopupMenu */
3287 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3288 if (executedMenuId == -1) executedMenuId = 0;
3289 return executedMenuId;
3292 /***********************************************************************
3293 * MENU_InitTracking
3295 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3297 POPUPMENU *menu;
3299 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3301 HideCaret(0);
3303 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3305 /* This makes the menus of applications built with Delphi work.
3306 * It also enables menus to be displayed in more than one window,
3307 * but there are some bugs left that need to be fixed in this case.
3309 if (!bPopup) menu->hWnd = hWnd;
3310 if (!top_popup)
3312 top_popup = menu->hWnd;
3313 top_popup_hmenu = hMenu;
3316 fEndMenu = FALSE;
3318 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3319 if (!(wFlags & TPM_NONOTIFY))
3320 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3322 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3324 if (!(wFlags & TPM_NONOTIFY))
3326 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3327 /* If an app changed/recreated menu bar entries in WM_INITMENU
3328 * menu sizes will be recalculated once the menu created/shown.
3332 return TRUE;
3335 /***********************************************************************
3336 * MENU_ExitTracking
3338 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3340 TRACE("hwnd=%p\n", hWnd);
3342 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3343 ShowCaret(0);
3344 top_popup = 0;
3345 top_popup_hmenu = NULL;
3346 return TRUE;
3349 /***********************************************************************
3350 * MENU_TrackMouseMenuBar
3352 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3354 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3356 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3357 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3359 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3361 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3362 if (IsMenu(hMenu))
3364 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3366 /* fetch the window menu again, it may have changed */
3367 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3368 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3369 MENU_ExitTracking(hWnd, FALSE);
3374 /***********************************************************************
3375 * MENU_TrackKbdMenuBar
3377 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3379 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3381 UINT uItem = NO_SELECTED_ITEM;
3382 HMENU hTrackMenu;
3383 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3385 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3387 /* find window that has a menu */
3389 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3390 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3392 /* check if we have to track a system menu */
3394 hTrackMenu = GetMenu( hwnd );
3395 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3397 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3398 hTrackMenu = get_win_sys_menu( hwnd );
3399 uItem = 0;
3400 wParam |= HTSYSMENU; /* prevent item lookup */
3402 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3404 if (!IsMenu( hTrackMenu )) return;
3406 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3408 /* fetch the window menu again, it may have changed */
3409 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3411 if( wChar && wChar != ' ' )
3413 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3414 if ( uItem >= (UINT)(-2) )
3416 if( uItem == (UINT)(-1) ) MessageBeep(0);
3417 /* schedule end of menu tracking */
3418 wFlags |= TF_ENDMENU;
3419 goto track_menu;
3423 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3425 if (!(wParam & HTSYSMENU) || wChar == ' ')
3427 if( uItem == NO_SELECTED_ITEM )
3428 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3429 else
3430 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3433 track_menu:
3434 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3435 MENU_ExitTracking( hwnd, FALSE );
3438 /**********************************************************************
3439 * TrackPopupMenuEx (USER32.@)
3441 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3442 HWND hWnd, LPTPMPARAMS lpTpm )
3444 POPUPMENU *menu;
3445 BOOL ret = FALSE;
3447 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3448 hMenu, wFlags, x, y, hWnd, lpTpm,
3449 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3451 /* Parameter check */
3452 /* FIXME: this check is performed several times, here and in the called
3453 functions. That could be optimized */
3454 if (!(menu = MENU_GetMenu( hMenu )))
3456 SetLastError( ERROR_INVALID_MENU_HANDLE );
3457 return FALSE;
3460 if (IsWindow(menu->hWnd))
3462 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3463 return FALSE;
3466 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3468 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3470 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3471 if (!(wFlags & TPM_NONOTIFY))
3472 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3474 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3475 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3476 lpTpm ? &lpTpm->rcExclude : NULL );
3477 MENU_ExitTracking(hWnd, TRUE);
3479 if (menu->hWnd)
3481 DestroyWindow( menu->hWnd );
3482 menu->hWnd = 0;
3484 if (!(wFlags & TPM_NONOTIFY))
3485 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3486 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3490 return ret;
3493 /**********************************************************************
3494 * TrackPopupMenu (USER32.@)
3496 * Like the win32 API, the function return the command ID only if the
3497 * flag TPM_RETURNCMD is on.
3500 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3501 INT nReserved, HWND hWnd, const RECT *lpRect )
3503 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3506 /***********************************************************************
3507 * PopupMenuWndProc
3509 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3511 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3513 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3515 switch(message)
3517 case WM_CREATE:
3519 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3520 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3521 return 0;
3524 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3525 return MA_NOACTIVATE;
3527 case WM_PAINT:
3529 PAINTSTRUCT ps;
3530 BeginPaint( hwnd, &ps );
3531 MENU_DrawPopupMenu( hwnd, ps.hdc,
3532 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3533 EndPaint( hwnd, &ps );
3534 return 0;
3537 case WM_PRINTCLIENT:
3539 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3540 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3541 return 0;
3544 case WM_ERASEBKGND:
3545 return 1;
3547 case WM_DESTROY:
3548 /* zero out global pointer in case resident popup window was destroyed. */
3549 if (hwnd == top_popup) {
3550 top_popup = 0;
3551 top_popup_hmenu = NULL;
3553 break;
3555 case WM_SHOWWINDOW:
3557 if( wParam )
3559 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3561 else
3562 SetWindowLongPtrW( hwnd, 0, 0 );
3563 break;
3565 case MN_GETHMENU:
3566 return GetWindowLongPtrW( hwnd, 0 );
3568 default:
3569 return DefWindowProcW( hwnd, message, wParam, lParam );
3571 return 0;
3575 /***********************************************************************
3576 * MENU_GetMenuBarHeight
3578 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3580 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3581 INT orgX, INT orgY )
3583 HDC hdc;
3584 RECT rectBar;
3585 LPPOPUPMENU lppop;
3587 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3589 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3591 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3592 SelectObject( hdc, get_menu_font(FALSE));
3593 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3594 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3595 ReleaseDC( hwnd, hdc );
3596 return lppop->Height;
3600 /*******************************************************************
3601 * ChangeMenuA (USER32.@)
3603 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3604 UINT id, UINT flags )
3606 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3607 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3608 id, data );
3609 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3610 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3611 id, data );
3612 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3613 flags & MF_BYPOSITION ? pos : id,
3614 flags & ~MF_REMOVE );
3615 /* Default: MF_INSERT */
3616 return InsertMenuA( hMenu, pos, flags, id, data );
3620 /*******************************************************************
3621 * ChangeMenuW (USER32.@)
3623 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3624 UINT id, UINT flags )
3626 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3627 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3628 id, data );
3629 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3630 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3631 id, data );
3632 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3633 flags & MF_BYPOSITION ? pos : id,
3634 flags & ~MF_REMOVE );
3635 /* Default: MF_INSERT */
3636 return InsertMenuW( hMenu, pos, flags, id, data );
3640 /*******************************************************************
3641 * CheckMenuItem (USER32.@)
3643 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3645 MENUITEM *item;
3646 DWORD ret;
3648 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3649 ret = item->fState & MF_CHECKED;
3650 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3651 else item->fState &= ~MF_CHECKED;
3652 return ret;
3656 /**********************************************************************
3657 * EnableMenuItem (USER32.@)
3659 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3661 UINT oldflags;
3662 MENUITEM *item;
3663 POPUPMENU *menu;
3665 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3667 /* Get the Popupmenu to access the owner menu */
3668 if (!(menu = MENU_GetMenu(hMenu)))
3669 return (UINT)-1;
3671 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3672 return (UINT)-1;
3674 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3675 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3677 /* If the close item in the system menu change update the close button */
3678 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3680 if (menu->hSysMenuOwner != 0)
3682 RECT rc;
3683 POPUPMENU* parentMenu;
3685 /* Get the parent menu to access*/
3686 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3687 return (UINT)-1;
3689 /* Refresh the frame to reflect the change */
3690 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3691 rc.bottom = 0;
3692 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3696 return oldflags;
3700 /*******************************************************************
3701 * GetMenuStringA (USER32.@)
3703 INT WINAPI GetMenuStringA(
3704 HMENU hMenu, /* [in] menuhandle */
3705 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3706 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3707 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3708 UINT wFlags /* [in] MF_ flags */
3710 MENUITEM *item;
3712 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3713 if (str && nMaxSiz) str[0] = '\0';
3714 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3715 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3716 return 0;
3718 if (!item->text) return 0;
3719 if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3720 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3721 str[nMaxSiz-1] = 0;
3722 TRACE("returning %s\n", debugstr_a(str));
3723 return strlen(str);
3727 /*******************************************************************
3728 * GetMenuStringW (USER32.@)
3730 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3731 LPWSTR str, INT nMaxSiz, UINT wFlags )
3733 MENUITEM *item;
3735 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3736 if (str && nMaxSiz) str[0] = '\0';
3737 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3738 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3739 return 0;
3741 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3742 if( !(item->text)) {
3743 str[0] = 0;
3744 return 0;
3746 lstrcpynW( str, item->text, nMaxSiz );
3747 TRACE("returning %s\n", debugstr_w(str));
3748 return strlenW(str);
3752 /**********************************************************************
3753 * HiliteMenuItem (USER32.@)
3755 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3756 UINT wHilite )
3758 LPPOPUPMENU menu;
3759 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3760 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3761 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3762 if (menu->FocusedItem == wItemID) return TRUE;
3763 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3764 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3765 return TRUE;
3769 /**********************************************************************
3770 * GetMenuState (USER32.@)
3772 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3774 MENUITEM *item;
3775 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3776 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3777 debug_print_menuitem (" item: ", item, "");
3778 if (item->fType & MF_POPUP)
3780 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3781 if (!menu) return -1;
3782 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3784 else
3786 /* We used to (from way back then) mask the result to 0xff. */
3787 /* I don't know why and it seems wrong as the documented */
3788 /* return flag MF_SEPARATOR is outside that mask. */
3789 return (item->fType | item->fState);
3794 /**********************************************************************
3795 * GetMenuItemCount (USER32.@)
3797 INT WINAPI GetMenuItemCount( HMENU hMenu )
3799 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3800 if (!menu) return -1;
3801 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3802 return menu->nItems;
3806 /**********************************************************************
3807 * GetMenuItemID (USER32.@)
3809 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3811 MENUITEM * lpmi;
3813 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3814 if (lpmi->fType & MF_POPUP) return -1;
3815 return lpmi->wID;
3820 /**********************************************************************
3821 * MENU_mnu2mnuii
3823 * Uses flags, id and text ptr, passed by InsertMenu() and
3824 * ModifyMenu() to setup a MenuItemInfo structure.
3826 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3827 LPMENUITEMINFOW pmii)
3829 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3830 pmii->cbSize = sizeof( MENUITEMINFOW);
3831 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3832 /* setting bitmap clears text and vice versa */
3833 if( IS_STRING_ITEM(flags)) {
3834 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3835 if( !str)
3836 flags |= MF_SEPARATOR;
3837 /* Item beginning with a backspace is a help item */
3838 /* FIXME: wrong place, this is only true in win16 */
3839 else if( *str == '\b') {
3840 flags |= MF_HELP;
3841 str++;
3843 pmii->dwTypeData = (LPWSTR)str;
3844 } else if( flags & MFT_BITMAP){
3845 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3846 pmii->hbmpItem = (HBITMAP)str;
3848 if( flags & MF_OWNERDRAW){
3849 pmii->fMask |= MIIM_DATA;
3850 pmii->dwItemData = (ULONG_PTR) str;
3852 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3853 pmii->fMask |= MIIM_SUBMENU;
3854 pmii->hSubMenu = (HMENU)id;
3856 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3857 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3858 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3859 pmii->wID = (UINT)id;
3863 /*******************************************************************
3864 * InsertMenuW (USER32.@)
3866 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3867 UINT_PTR id, LPCWSTR str )
3869 MENUITEM *item;
3870 MENUITEMINFOW mii;
3872 if (IS_STRING_ITEM(flags) && str)
3873 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3874 hMenu, pos, flags, id, debugstr_w(str) );
3875 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3876 hMenu, pos, flags, id, str );
3878 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3879 MENU_mnu2mnuii( flags, id, str, &mii);
3880 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3882 RemoveMenu( hMenu, pos, flags );
3883 return FALSE;
3886 item->hCheckBit = item->hUnCheckBit = 0;
3887 return TRUE;
3891 /*******************************************************************
3892 * InsertMenuA (USER32.@)
3894 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3895 UINT_PTR id, LPCSTR str )
3897 BOOL ret = FALSE;
3899 if (IS_STRING_ITEM(flags) && str)
3901 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3902 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3903 if (newstr)
3905 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3906 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3907 HeapFree( GetProcessHeap(), 0, newstr );
3909 return ret;
3911 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3915 /*******************************************************************
3916 * AppendMenuA (USER32.@)
3918 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3919 UINT_PTR id, LPCSTR data )
3921 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3925 /*******************************************************************
3926 * AppendMenuW (USER32.@)
3928 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3929 UINT_PTR id, LPCWSTR data )
3931 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3935 /**********************************************************************
3936 * RemoveMenu (USER32.@)
3938 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3940 LPPOPUPMENU menu;
3941 MENUITEM *item;
3943 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3944 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3945 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3947 /* Remove item */
3949 MENU_FreeItemData( item );
3951 if (--menu->nItems == 0)
3953 HeapFree( GetProcessHeap(), 0, menu->items );
3954 menu->items = NULL;
3956 else
3958 while(nPos < menu->nItems)
3960 *item = *(item+1);
3961 item++;
3962 nPos++;
3964 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3965 menu->nItems * sizeof(MENUITEM) );
3967 return TRUE;
3971 /**********************************************************************
3972 * DeleteMenu (USER32.@)
3974 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3976 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3977 if (!item) return FALSE;
3978 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3979 /* nPos is now the position of the item */
3980 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3981 return TRUE;
3985 /*******************************************************************
3986 * ModifyMenuW (USER32.@)
3988 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3989 UINT_PTR id, LPCWSTR str )
3991 MENUITEM *item;
3992 MENUITEMINFOW mii;
3994 if (IS_STRING_ITEM(flags))
3995 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3996 else
3997 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3999 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
4000 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4001 MENU_mnu2mnuii( flags, id, str, &mii);
4002 return SetMenuItemInfo_common( item, &mii, TRUE);
4006 /*******************************************************************
4007 * ModifyMenuA (USER32.@)
4009 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4010 UINT_PTR id, LPCSTR str )
4012 BOOL ret = FALSE;
4014 if (IS_STRING_ITEM(flags) && str)
4016 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4017 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4018 if (newstr)
4020 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4021 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4022 HeapFree( GetProcessHeap(), 0, newstr );
4024 return ret;
4026 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4030 /**********************************************************************
4031 * CreatePopupMenu (USER32.@)
4033 HMENU WINAPI CreatePopupMenu(void)
4035 HMENU hmenu;
4036 POPUPMENU *menu;
4038 if (!(hmenu = CreateMenu())) return 0;
4039 menu = MENU_GetMenu( hmenu );
4040 menu->wFlags |= MF_POPUP;
4041 menu->bTimeToHide = FALSE;
4042 return hmenu;
4046 /**********************************************************************
4047 * GetMenuCheckMarkDimensions (USER.417)
4048 * GetMenuCheckMarkDimensions (USER32.@)
4050 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4052 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4056 /**********************************************************************
4057 * SetMenuItemBitmaps (USER32.@)
4059 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4060 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4062 MENUITEM *item;
4064 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4066 if (!hNewCheck && !hNewUnCheck)
4068 item->fState &= ~MF_USECHECKBITMAPS;
4070 else /* Install new bitmaps */
4072 item->hCheckBit = hNewCheck;
4073 item->hUnCheckBit = hNewUnCheck;
4074 item->fState |= MF_USECHECKBITMAPS;
4076 return TRUE;
4080 /**********************************************************************
4081 * CreateMenu (USER32.@)
4083 HMENU WINAPI CreateMenu(void)
4085 HMENU hMenu;
4086 LPPOPUPMENU menu;
4088 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4089 menu->FocusedItem = NO_SELECTED_ITEM;
4090 menu->bTimeToHide = FALSE;
4092 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4094 TRACE("return %p\n", hMenu );
4096 return hMenu;
4100 /**********************************************************************
4101 * DestroyMenu (USER32.@)
4103 BOOL WINAPI DestroyMenu( HMENU hMenu )
4105 LPPOPUPMENU lppop;
4107 TRACE("(%p)\n", hMenu);
4109 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4110 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4112 /* DestroyMenu should not destroy system menu popup owner */
4113 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4115 DestroyWindow( lppop->hWnd );
4116 lppop->hWnd = 0;
4119 if (lppop->items) /* recursively destroy submenus */
4121 int i;
4122 MENUITEM *item = lppop->items;
4123 for (i = lppop->nItems; i > 0; i--, item++)
4125 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4126 MENU_FreeItemData( item );
4128 HeapFree( GetProcessHeap(), 0, lppop->items );
4130 HeapFree( GetProcessHeap(), 0, lppop );
4131 return TRUE;
4135 /**********************************************************************
4136 * GetSystemMenu (USER32.@)
4138 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4140 WND *wndPtr = WIN_GetPtr( hWnd );
4141 HMENU retvalue = 0;
4143 if (wndPtr == WND_DESKTOP) return 0;
4144 if (wndPtr == WND_OTHER_PROCESS)
4146 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4148 else if (wndPtr)
4150 if (wndPtr->hSysMenu && bRevert)
4152 DestroyMenu(wndPtr->hSysMenu);
4153 wndPtr->hSysMenu = 0;
4156 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4157 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4159 if( wndPtr->hSysMenu )
4161 POPUPMENU *menu;
4162 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4164 /* Store the dummy sysmenu handle to facilitate the refresh */
4165 /* of the close button if the SC_CLOSE item change */
4166 menu = MENU_GetMenu(retvalue);
4167 if ( menu )
4168 menu->hSysMenuOwner = wndPtr->hSysMenu;
4170 WIN_ReleasePtr( wndPtr );
4172 return bRevert ? 0 : retvalue;
4176 /*******************************************************************
4177 * SetSystemMenu (USER32.@)
4179 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4181 WND *wndPtr = WIN_GetPtr( hwnd );
4183 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4185 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4186 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4187 WIN_ReleasePtr( wndPtr );
4188 return TRUE;
4190 return FALSE;
4194 /**********************************************************************
4195 * GetMenu (USER32.@)
4197 HMENU WINAPI GetMenu( HWND hWnd )
4199 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4200 TRACE("for %p returning %p\n", hWnd, retvalue);
4201 return retvalue;
4204 /**********************************************************************
4205 * GetMenuBarInfo (USER32.@)
4207 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4209 POPUPMENU *menu;
4210 HMENU hmenu = NULL;
4211 ATOM class_atom;
4213 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4215 switch (idObject)
4217 case OBJID_CLIENT:
4218 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4219 if (!class_atom)
4220 return FALSE;
4221 if (class_atom != POPUPMENU_CLASS_ATOM)
4223 WARN("called on invalid window: %d\n", class_atom);
4224 SetLastError(ERROR_INVALID_MENU_HANDLE);
4225 return FALSE;
4228 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4229 break;
4230 case OBJID_MENU:
4231 hmenu = GetMenu(hwnd);
4232 break;
4233 case OBJID_SYSMENU:
4234 hmenu = GetSystemMenu(hwnd, FALSE);
4235 break;
4236 default:
4237 return FALSE;
4240 if (!hmenu)
4241 return FALSE;
4243 if (pmbi->cbSize != sizeof(MENUBARINFO))
4245 SetLastError(ERROR_INVALID_PARAMETER);
4246 return FALSE;
4249 menu = MENU_GetMenu(hmenu);
4250 if (!menu)
4251 return FALSE;
4252 if (idItem < 0 || idItem > menu->nItems)
4253 return FALSE;
4255 if (!menu->Height)
4257 SetRectEmpty(&pmbi->rcBar);
4259 else if (idItem == 0)
4261 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4262 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4263 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4265 else
4267 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4270 pmbi->hMenu = hmenu;
4271 pmbi->hwndMenu = NULL;
4272 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4273 if (idItem)
4275 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4276 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4278 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4279 if (menu)
4280 pmbi->hwndMenu = menu->hWnd;
4283 else
4285 pmbi->fFocused = pmbi->fBarFocused;
4288 return TRUE;
4291 /**********************************************************************
4292 * MENU_SetMenu
4294 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4295 * SetWindowPos call that would result if SetMenu were called directly.
4297 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4299 TRACE("(%p, %p);\n", hWnd, hMenu);
4301 if (hMenu && !IsMenu(hMenu))
4303 WARN("hMenu %p is not a menu handle\n", hMenu);
4304 return FALSE;
4306 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4307 return FALSE;
4309 hWnd = WIN_GetFullHandle( hWnd );
4310 if (GetCapture() == hWnd)
4311 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4313 if (hMenu != 0)
4315 LPPOPUPMENU lpmenu;
4317 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4319 lpmenu->hWnd = hWnd;
4320 lpmenu->Height = 0; /* Make sure we recalculate the size */
4322 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4323 return TRUE;
4327 /**********************************************************************
4328 * SetMenu (USER32.@)
4330 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4332 if(!MENU_SetMenu(hWnd, hMenu))
4333 return FALSE;
4335 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4336 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4337 return TRUE;
4341 /**********************************************************************
4342 * GetSubMenu (USER32.@)
4344 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4346 MENUITEM * lpmi;
4348 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4349 if (!(lpmi->fType & MF_POPUP)) return 0;
4350 return lpmi->hSubMenu;
4354 /**********************************************************************
4355 * DrawMenuBar (USER32.@)
4357 BOOL WINAPI DrawMenuBar( HWND hWnd )
4359 LPPOPUPMENU lppop;
4360 HMENU hMenu;
4362 if (!IsWindow( hWnd ))
4363 return FALSE;
4364 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4365 return TRUE;
4367 if ((hMenu = GetMenu( hWnd )) && (lppop = MENU_GetMenu( hMenu ))) {
4368 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4369 lppop->hwndOwner = hWnd;
4372 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4373 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4376 /***********************************************************************
4377 * DrawMenuBarTemp (USER32.@)
4379 * UNDOCUMENTED !!
4381 * called by W98SE desk.cpl Control Panel Applet
4383 * Not 100% sure about the param names, but close.
4385 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4387 LPPOPUPMENU lppop;
4388 UINT i,retvalue;
4389 HFONT hfontOld = 0;
4390 BOOL flat_menu = FALSE;
4392 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4394 if (!hMenu)
4395 hMenu = GetMenu(hwnd);
4397 if (!hFont)
4398 hFont = get_menu_font(FALSE);
4400 lppop = MENU_GetMenu( hMenu );
4401 if (lppop == NULL || lprect == NULL)
4403 retvalue = GetSystemMetrics(SM_CYMENU);
4404 goto END;
4407 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4409 hfontOld = SelectObject( hDC, hFont);
4411 if (lppop->Height == 0)
4412 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4414 lprect->bottom = lprect->top + lppop->Height;
4416 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4418 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4419 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4420 LineTo( hDC, lprect->right, lprect->bottom );
4422 if (lppop->nItems == 0)
4424 retvalue = GetSystemMetrics(SM_CYMENU);
4425 goto END;
4428 for (i = 0; i < lppop->nItems; i++)
4430 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4431 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4433 retvalue = lppop->Height;
4435 END:
4436 if (hfontOld) SelectObject (hDC, hfontOld);
4437 return retvalue;
4440 /***********************************************************************
4441 * EndMenu (USER.187)
4442 * EndMenu (USER32.@)
4444 BOOL WINAPI EndMenu(void)
4446 /* if we are in the menu code, and it is active */
4447 if (!fEndMenu && top_popup)
4449 /* terminate the menu handling code */
4450 fEndMenu = TRUE;
4452 /* needs to be posted to wakeup the internal menu handler */
4453 /* which will now terminate the menu, in the event that */
4454 /* the main window was minimized, or lost focus, so we */
4455 /* don't end up with an orphaned menu */
4456 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4458 return fEndMenu;
4462 /*****************************************************************
4463 * LoadMenuA (USER32.@)
4465 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4467 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4468 if (!hrsrc) return 0;
4469 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4473 /*****************************************************************
4474 * LoadMenuW (USER32.@)
4476 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4478 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4479 if (!hrsrc) return 0;
4480 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4484 /**********************************************************************
4485 * LoadMenuIndirectW (USER32.@)
4487 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4489 HMENU hMenu;
4490 WORD version, offset;
4491 LPCSTR p = template;
4493 version = GET_WORD(p);
4494 p += sizeof(WORD);
4495 TRACE("%p, ver %d\n", template, version );
4496 switch (version)
4498 case 0: /* standard format is version of 0 */
4499 offset = GET_WORD(p);
4500 p += sizeof(WORD) + offset;
4501 if (!(hMenu = CreateMenu())) return 0;
4502 if (!MENU_ParseResource( p, hMenu ))
4504 DestroyMenu( hMenu );
4505 return 0;
4507 return hMenu;
4508 case 1: /* extended format is version of 1 */
4509 offset = GET_WORD(p);
4510 p += sizeof(WORD) + offset;
4511 if (!(hMenu = CreateMenu())) return 0;
4512 if (!MENUEX_ParseResource( p, hMenu))
4514 DestroyMenu( hMenu );
4515 return 0;
4517 return hMenu;
4518 default:
4519 ERR("version %d not supported.\n", version);
4520 return 0;
4525 /**********************************************************************
4526 * LoadMenuIndirectA (USER32.@)
4528 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4530 return LoadMenuIndirectW( template );
4534 /**********************************************************************
4535 * IsMenu (USER32.@)
4537 BOOL WINAPI IsMenu(HMENU hmenu)
4539 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4541 if (!menu)
4543 SetLastError(ERROR_INVALID_MENU_HANDLE);
4544 return FALSE;
4546 return TRUE;
4549 /**********************************************************************
4550 * GetMenuItemInfo_common
4553 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4554 LPMENUITEMINFOW lpmii, BOOL unicode)
4556 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4558 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4560 if (!menu) {
4561 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4562 return FALSE;
4565 if( lpmii->fMask & MIIM_TYPE) {
4566 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4567 WARN("invalid combination of fMask bits used\n");
4568 /* this does not happen on Win9x/ME */
4569 SetLastError( ERROR_INVALID_PARAMETER);
4570 return FALSE;
4572 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4573 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4574 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4575 if( lpmii->fType & MFT_BITMAP) {
4576 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4577 lpmii->cch = 0;
4578 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4579 /* this does not happen on Win9x/ME */
4580 lpmii->dwTypeData = 0;
4581 lpmii->cch = 0;
4585 /* copy the text string */
4586 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4587 if( !menu->text ) {
4588 if(lpmii->dwTypeData && lpmii->cch) {
4589 if( unicode)
4590 *((WCHAR *)lpmii->dwTypeData) = 0;
4591 else
4592 *((CHAR *)lpmii->dwTypeData) = 0;
4594 lpmii->cch = 0;
4595 } else {
4596 int len;
4597 if (unicode)
4599 len = strlenW(menu->text);
4600 if(lpmii->dwTypeData && lpmii->cch)
4601 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4603 else
4605 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4606 0, NULL, NULL ) - 1;
4607 if(lpmii->dwTypeData && lpmii->cch)
4608 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4609 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4610 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4612 /* if we've copied a substring we return its length */
4613 if(lpmii->dwTypeData && lpmii->cch)
4614 if (lpmii->cch <= len + 1)
4615 lpmii->cch--;
4616 else
4617 lpmii->cch = len;
4618 else {
4619 /* return length of string */
4620 /* not on Win9x/ME if fType & MFT_BITMAP */
4621 lpmii->cch = len;
4626 if (lpmii->fMask & MIIM_FTYPE)
4627 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4629 if (lpmii->fMask & MIIM_BITMAP)
4630 lpmii->hbmpItem = menu->hbmpItem;
4632 if (lpmii->fMask & MIIM_STATE)
4633 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4635 if (lpmii->fMask & MIIM_ID)
4636 lpmii->wID = menu->wID;
4638 if (lpmii->fMask & MIIM_SUBMENU)
4639 lpmii->hSubMenu = menu->hSubMenu;
4640 else {
4641 /* hSubMenu is always cleared
4642 * (not on Win9x/ME ) */
4643 lpmii->hSubMenu = 0;
4646 if (lpmii->fMask & MIIM_CHECKMARKS) {
4647 lpmii->hbmpChecked = menu->hCheckBit;
4648 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4650 if (lpmii->fMask & MIIM_DATA)
4651 lpmii->dwItemData = menu->dwItemData;
4653 return TRUE;
4656 /**********************************************************************
4657 * GetMenuItemInfoA (USER32.@)
4659 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4660 LPMENUITEMINFOA lpmii)
4662 BOOL ret;
4663 MENUITEMINFOA mii;
4664 if( lpmii->cbSize != sizeof( mii) &&
4665 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4666 SetLastError( ERROR_INVALID_PARAMETER);
4667 return FALSE;
4669 memcpy( &mii, lpmii, lpmii->cbSize);
4670 mii.cbSize = sizeof( mii);
4671 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4672 (LPMENUITEMINFOW)&mii, FALSE);
4673 mii.cbSize = lpmii->cbSize;
4674 memcpy( lpmii, &mii, mii.cbSize);
4675 return ret;
4678 /**********************************************************************
4679 * GetMenuItemInfoW (USER32.@)
4681 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4682 LPMENUITEMINFOW lpmii)
4684 BOOL ret;
4685 MENUITEMINFOW mii;
4686 if( lpmii->cbSize != sizeof( mii) &&
4687 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4688 SetLastError( ERROR_INVALID_PARAMETER);
4689 return FALSE;
4691 memcpy( &mii, lpmii, lpmii->cbSize);
4692 mii.cbSize = sizeof( mii);
4693 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4694 mii.cbSize = lpmii->cbSize;
4695 memcpy( lpmii, &mii, mii.cbSize);
4696 return ret;
4700 /* set a menu item text from a ASCII or Unicode string */
4701 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4703 if (!text)
4704 menu->text = NULL;
4705 else if (unicode)
4707 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4708 strcpyW( menu->text, text );
4710 else
4712 LPCSTR str = (LPCSTR)text;
4713 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4714 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4715 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4720 /**********************************************************************
4721 * MENU_depth
4723 * detect if there are loops in the menu tree (or the depth is too large)
4725 static int MENU_depth( POPUPMENU *pmenu, int depth)
4727 UINT i;
4728 MENUITEM *item;
4729 int subdepth;
4731 depth++;
4732 if( depth > MAXMENUDEPTH) return depth;
4733 item = pmenu->items;
4734 subdepth = depth;
4735 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4736 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4737 if( psubmenu){
4738 int bdepth = MENU_depth( psubmenu, depth);
4739 if( bdepth > subdepth) subdepth = bdepth;
4741 if( subdepth > MAXMENUDEPTH)
4742 TRACE("<- hmenu %p\n", item->hSubMenu);
4744 return subdepth;
4748 /**********************************************************************
4749 * SetMenuItemInfo_common
4751 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4752 * MIIM_BITMAP and MIIM_STRING flags instead.
4755 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4756 const MENUITEMINFOW *lpmii,
4757 BOOL unicode)
4759 if (!menu) return FALSE;
4761 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4763 if (lpmii->fMask & MIIM_FTYPE ) {
4764 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4765 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4767 if (lpmii->fMask & MIIM_STRING ) {
4768 /* free the string when used */
4769 HeapFree(GetProcessHeap(), 0, menu->text);
4770 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4773 if (lpmii->fMask & MIIM_STATE)
4774 /* Other menu items having MFS_DEFAULT are not converted
4775 to normal items */
4776 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4778 if (lpmii->fMask & MIIM_ID)
4779 menu->wID = lpmii->wID;
4781 if (lpmii->fMask & MIIM_SUBMENU) {
4782 menu->hSubMenu = lpmii->hSubMenu;
4783 if (menu->hSubMenu) {
4784 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4785 if (subMenu) {
4786 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4787 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4788 menu->hSubMenu = 0;
4789 return FALSE;
4791 subMenu->wFlags |= MF_POPUP;
4792 menu->fType |= MF_POPUP;
4793 } else {
4794 SetLastError( ERROR_INVALID_PARAMETER);
4795 return FALSE;
4798 else
4799 menu->fType &= ~MF_POPUP;
4802 if (lpmii->fMask & MIIM_CHECKMARKS)
4804 menu->hCheckBit = lpmii->hbmpChecked;
4805 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4807 if (lpmii->fMask & MIIM_DATA)
4808 menu->dwItemData = lpmii->dwItemData;
4810 if (lpmii->fMask & MIIM_BITMAP)
4811 menu->hbmpItem = lpmii->hbmpItem;
4813 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4814 menu->fType |= MFT_SEPARATOR;
4816 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4817 return TRUE;
4820 /**********************************************************************
4821 * MENU_NormalizeMenuItemInfoStruct
4823 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4824 * check, copy and extend the MENUITEMINFO struct from the version that the application
4825 * supplied to the version used by wine source. */
4826 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4827 MENUITEMINFOW *pmii_out )
4829 /* do we recognize the size? */
4830 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4831 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4832 SetLastError( ERROR_INVALID_PARAMETER);
4833 return FALSE;
4835 /* copy the fields that we have */
4836 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4837 /* if the hbmpItem member is missing then extend */
4838 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4839 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4840 pmii_out->hbmpItem = NULL;
4842 /* test for invalid bit combinations */
4843 if( (pmii_out->fMask & MIIM_TYPE &&
4844 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4845 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4846 WARN("invalid combination of fMask bits used\n");
4847 /* this does not happen on Win9x/ME */
4848 SetLastError( ERROR_INVALID_PARAMETER);
4849 return FALSE;
4851 /* convert old style (MIIM_TYPE) to the new */
4852 if( pmii_out->fMask & MIIM_TYPE){
4853 pmii_out->fMask |= MIIM_FTYPE;
4854 if( IS_STRING_ITEM(pmii_out->fType)){
4855 pmii_out->fMask |= MIIM_STRING;
4856 } else if( (pmii_out->fType) & MFT_BITMAP){
4857 pmii_out->fMask |= MIIM_BITMAP;
4858 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4861 return TRUE;
4864 /**********************************************************************
4865 * SetMenuItemInfoA (USER32.@)
4867 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4868 const MENUITEMINFOA *lpmii)
4870 MENUITEMINFOW mii;
4872 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4874 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4876 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4877 &mii, FALSE);
4880 /**********************************************************************
4881 * SetMenuItemInfoW (USER32.@)
4883 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4884 const MENUITEMINFOW *lpmii)
4886 MENUITEMINFOW mii;
4888 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4890 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4891 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4892 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4895 /**********************************************************************
4896 * SetMenuDefaultItem (USER32.@)
4899 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4901 UINT i;
4902 POPUPMENU *menu;
4903 MENUITEM *item;
4905 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4907 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4909 /* reset all default-item flags */
4910 item = menu->items;
4911 for (i = 0; i < menu->nItems; i++, item++)
4913 item->fState &= ~MFS_DEFAULT;
4916 /* no default item */
4917 if ( -1 == uItem)
4919 return TRUE;
4922 item = menu->items;
4923 if ( bypos )
4925 if ( uItem >= menu->nItems ) return FALSE;
4926 item[uItem].fState |= MFS_DEFAULT;
4927 return TRUE;
4929 else
4931 for (i = 0; i < menu->nItems; i++, item++)
4933 if (item->wID == uItem)
4935 item->fState |= MFS_DEFAULT;
4936 return TRUE;
4941 return FALSE;
4944 /**********************************************************************
4945 * GetMenuDefaultItem (USER32.@)
4947 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4949 POPUPMENU *menu;
4950 MENUITEM * item;
4951 UINT i = 0;
4953 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4955 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4957 /* find default item */
4958 item = menu->items;
4960 /* empty menu */
4961 if (! item) return -1;
4963 while ( !( item->fState & MFS_DEFAULT ) )
4965 i++; item++;
4966 if (i >= menu->nItems ) return -1;
4969 /* default: don't return disabled items */
4970 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4972 /* search rekursiv when needed */
4973 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4975 UINT ret;
4976 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4977 if ( -1 != ret ) return ret;
4979 /* when item not found in submenu, return the popup item */
4981 return ( bypos ) ? i : item->wID;
4986 /**********************************************************************
4987 * InsertMenuItemA (USER32.@)
4989 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4990 const MENUITEMINFOA *lpmii)
4992 MENUITEM *item;
4993 MENUITEMINFOW mii;
4995 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4997 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4999 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5000 return SetMenuItemInfo_common(item, &mii, FALSE);
5004 /**********************************************************************
5005 * InsertMenuItemW (USER32.@)
5007 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5008 const MENUITEMINFOW *lpmii)
5010 MENUITEM *item;
5011 MENUITEMINFOW mii;
5013 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5015 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5017 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5018 return SetMenuItemInfo_common(item, &mii, TRUE);
5021 /**********************************************************************
5022 * CheckMenuRadioItem (USER32.@)
5025 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5026 UINT first, UINT last, UINT check,
5027 UINT bypos)
5029 BOOL done = FALSE;
5030 UINT i;
5031 MENUITEM *mi_first = NULL, *mi_check;
5032 HMENU m_first, m_check;
5034 for (i = first; i <= last; i++)
5036 UINT pos = i;
5038 if (!mi_first)
5040 m_first = hMenu;
5041 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5042 if (!mi_first) continue;
5043 mi_check = mi_first;
5044 m_check = m_first;
5046 else
5048 m_check = hMenu;
5049 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5050 if (!mi_check) continue;
5053 if (m_first != m_check) continue;
5054 if (mi_check->fType == MFT_SEPARATOR) continue;
5056 if (i == check)
5058 mi_check->fType |= MFT_RADIOCHECK;
5059 mi_check->fState |= MFS_CHECKED;
5060 done = TRUE;
5062 else
5064 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5065 mi_check->fState &= ~MFS_CHECKED;
5069 return done;
5073 /**********************************************************************
5074 * GetMenuItemRect (USER32.@)
5076 * ATTENTION: Here, the returned values in rect are the screen
5077 * coordinates of the item just like if the menu was
5078 * always on the upper left side of the application.
5081 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5082 LPRECT rect)
5084 POPUPMENU *itemMenu;
5085 MENUITEM *item;
5086 HWND referenceHwnd;
5088 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5090 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5091 referenceHwnd = hwnd;
5093 if(!hwnd)
5095 itemMenu = MENU_GetMenu(hMenu);
5096 if (itemMenu == NULL)
5097 return FALSE;
5099 if(itemMenu->hWnd == 0)
5100 return FALSE;
5101 referenceHwnd = itemMenu->hWnd;
5104 if ((rect == NULL) || (item == NULL))
5105 return FALSE;
5107 *rect = item->rect;
5109 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5111 return TRUE;
5114 /**********************************************************************
5115 * SetMenuInfo (USER32.@)
5117 * FIXME
5118 * actually use the items to draw the menu
5119 * (recalculate and/or redraw)
5121 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5123 POPUPMENU *menu;
5124 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5126 if (lpmi->fMask & MIM_BACKGROUND)
5127 menu->hbrBack = lpmi->hbrBack;
5129 if (lpmi->fMask & MIM_HELPID)
5130 menu->dwContextHelpID = lpmi->dwContextHelpID;
5132 if (lpmi->fMask & MIM_MAXHEIGHT)
5133 menu->cyMax = lpmi->cyMax;
5135 if (lpmi->fMask & MIM_MENUDATA)
5136 menu->dwMenuData = lpmi->dwMenuData;
5138 if (lpmi->fMask & MIM_STYLE)
5139 menu->dwStyle = lpmi->dwStyle;
5141 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5142 int i;
5143 MENUITEM *item = menu->items;
5144 for( i = menu->nItems; i; i--, item++)
5145 if( item->fType & MF_POPUP)
5146 menu_SetMenuInfo( item->hSubMenu, lpmi);
5148 return TRUE;
5151 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5153 TRACE("(%p %p)\n", hMenu, lpmi);
5154 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5155 if( lpmi->fMask & MIM_STYLE) {
5156 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5157 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5158 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5160 return TRUE;
5162 SetLastError( ERROR_INVALID_PARAMETER);
5163 return FALSE;
5166 /**********************************************************************
5167 * GetMenuInfo (USER32.@)
5169 * NOTES
5170 * win98/NT5.0
5173 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5174 { POPUPMENU *menu;
5176 TRACE("(%p %p)\n", hMenu, lpmi);
5178 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5181 if (lpmi->fMask & MIM_BACKGROUND)
5182 lpmi->hbrBack = menu->hbrBack;
5184 if (lpmi->fMask & MIM_HELPID)
5185 lpmi->dwContextHelpID = menu->dwContextHelpID;
5187 if (lpmi->fMask & MIM_MAXHEIGHT)
5188 lpmi->cyMax = menu->cyMax;
5190 if (lpmi->fMask & MIM_MENUDATA)
5191 lpmi->dwMenuData = menu->dwMenuData;
5193 if (lpmi->fMask & MIM_STYLE)
5194 lpmi->dwStyle = menu->dwStyle;
5196 return TRUE;
5198 SetLastError( ERROR_INVALID_PARAMETER);
5199 return FALSE;
5203 /**********************************************************************
5204 * SetMenuContextHelpId (USER32.@)
5206 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5208 LPPOPUPMENU menu;
5210 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5212 if ((menu = MENU_GetMenu(hMenu)))
5214 menu->dwContextHelpID = dwContextHelpID;
5215 return TRUE;
5217 return FALSE;
5221 /**********************************************************************
5222 * GetMenuContextHelpId (USER32.@)
5224 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5226 LPPOPUPMENU menu;
5228 TRACE("(%p)\n", hMenu);
5230 if ((menu = MENU_GetMenu(hMenu)))
5232 return menu->dwContextHelpID;
5234 return 0;
5237 /**********************************************************************
5238 * MenuItemFromPoint (USER32.@)
5240 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5242 POPUPMENU *menu = MENU_GetMenu(hMenu);
5243 UINT pos;
5245 /*FIXME: Do we have to handle hWnd here? */
5246 if (!menu) return -1;
5247 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5248 return pos;
5252 /**********************************************************************
5253 * translate_accelerator
5255 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5256 BYTE fVirt, WORD key, WORD cmd )
5258 INT mask = 0;
5259 UINT mesg = 0;
5261 if (wParam != key) return FALSE;
5263 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5264 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5265 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5267 if (message == WM_CHAR || message == WM_SYSCHAR)
5269 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5271 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5272 goto found;
5275 else
5277 if(fVirt & FVIRTKEY)
5279 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5280 wParam, 0xff & HIWORD(lParam));
5282 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5283 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5285 else
5287 if (!(lParam & 0x01000000)) /* no special_key */
5289 if ((fVirt & FALT) && (lParam & 0x20000000))
5290 { /* ^^ ALT pressed */
5291 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5292 goto found;
5297 return FALSE;
5299 found:
5300 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5301 mesg = 1;
5302 else
5304 HMENU hMenu, hSubMenu, hSysMenu;
5305 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5307 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5308 hSysMenu = get_win_sys_menu( hWnd );
5310 /* find menu item and ask application to initialize it */
5311 /* 1. in the system menu */
5312 hSubMenu = hSysMenu;
5313 nPos = cmd;
5314 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5316 if (GetCapture())
5317 mesg = 2;
5318 if (!IsWindowEnabled(hWnd))
5319 mesg = 3;
5320 else
5322 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5323 if(hSubMenu != hSysMenu)
5325 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5326 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5327 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5329 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5332 else /* 2. in the window's menu */
5334 hSubMenu = hMenu;
5335 nPos = cmd;
5336 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5338 if (GetCapture())
5339 mesg = 2;
5340 if (!IsWindowEnabled(hWnd))
5341 mesg = 3;
5342 else
5344 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5345 if(hSubMenu != hMenu)
5347 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5348 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5349 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5351 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5356 if (mesg == 0)
5358 if (uSysStat != (UINT)-1)
5360 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5361 mesg=4;
5362 else
5363 mesg=WM_SYSCOMMAND;
5365 else
5367 if (uStat != (UINT)-1)
5369 if (IsIconic(hWnd))
5370 mesg=5;
5371 else
5373 if (uStat & (MF_DISABLED|MF_GRAYED))
5374 mesg=6;
5375 else
5376 mesg=WM_COMMAND;
5379 else
5380 mesg=WM_COMMAND;
5385 if( mesg==WM_COMMAND )
5387 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5388 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5390 else if( mesg==WM_SYSCOMMAND )
5392 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5393 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5395 else
5397 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5398 * #0: unknown (please report!)
5399 * #1: for WM_KEYUP,WM_SYSKEYUP
5400 * #2: mouse is captured
5401 * #3: window is disabled
5402 * #4: it's a disabled system menu option
5403 * #5: it's a menu option, but window is iconic
5404 * #6: it's a menu option, but disabled
5406 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5407 if(mesg==0)
5408 ERR_(accel)(" unknown reason - please report!\n");
5410 return TRUE;
5413 /**********************************************************************
5414 * TranslateAcceleratorA (USER32.@)
5415 * TranslateAccelerator (USER32.@)
5417 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5419 switch (msg->message)
5421 case WM_KEYDOWN:
5422 case WM_SYSKEYDOWN:
5423 return TranslateAcceleratorW( hWnd, hAccel, msg );
5425 case WM_CHAR:
5426 case WM_SYSCHAR:
5428 MSG msgW = *msg;
5429 char ch = LOWORD(msg->wParam);
5430 WCHAR wch;
5431 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5432 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5433 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5436 default:
5437 return 0;
5441 /**********************************************************************
5442 * TranslateAcceleratorW (USER32.@)
5444 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5446 ACCEL data[32], *ptr = data;
5447 int i, count;
5449 if (!hWnd) return 0;
5451 if (msg->message != WM_KEYDOWN &&
5452 msg->message != WM_SYSKEYDOWN &&
5453 msg->message != WM_CHAR &&
5454 msg->message != WM_SYSCHAR)
5455 return 0;
5457 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5458 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5460 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5461 if (count > sizeof(data)/sizeof(data[0]))
5463 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5465 count = CopyAcceleratorTableW( hAccel, ptr, count );
5466 for (i = 0; i < count; i++)
5468 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5469 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5470 break;
5472 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5473 return (i < count);