dpnet/tests: Add a trailing '\n' to some ok() calls.
[wine.git] / dlls / user32 / menu.c
blobf0eb66bda7999c099112b5ec36d378886780ed46
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;
1741 RECT rect;
1743 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1745 GetClientRect( hwnd, &rect );
1747 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1748 && (SelectObject( hdc, get_menu_font(FALSE))))
1750 HPEN hPrevPen;
1752 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1754 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1755 if( hPrevPen )
1757 POPUPMENU *menu;
1758 BOOL flat_menu = FALSE;
1760 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1761 if (flat_menu)
1762 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1763 else
1764 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1766 if( (menu = MENU_GetMenu( hmenu )))
1768 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1769 /* draw menu items */
1770 if( menu->nItems)
1772 MENUITEM *item;
1773 UINT u;
1775 item = menu->items;
1776 for( u = menu->nItems; u > 0; u--, item++)
1777 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1778 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1780 /* draw scroll arrows */
1781 if (menu->bScrolling)
1782 MENU_DrawScrollArrows(menu, hdc);
1784 } else
1786 SelectObject( hdc, hPrevBrush );
1791 /***********************************************************************
1792 * MENU_DrawMenuBar
1794 * Paint a menu bar. Returns the height of the menu bar.
1795 * called from [windows/nonclient.c]
1797 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1799 LPPOPUPMENU lppop;
1800 HMENU hMenu = GetMenu(hwnd);
1802 lppop = MENU_GetMenu( hMenu );
1803 if (lppop == NULL || lprect == NULL)
1805 return GetSystemMetrics(SM_CYMENU);
1808 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1812 /***********************************************************************
1813 * MENU_InitPopup
1815 * Popup menu initialization before WM_ENTERMENULOOP.
1817 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1819 POPUPMENU *menu;
1820 DWORD ex_style = 0;
1822 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1824 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1826 /* store the owner for DrawItem */
1827 if (!IsWindow( hwndOwner ))
1829 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1830 return FALSE;
1832 menu->hwndOwner = hwndOwner;
1834 if (flags & TPM_LAYOUTRTL)
1835 ex_style = WS_EX_LAYOUTRTL;
1837 /* NOTE: In Windows, top menu popup is not owned. */
1838 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1839 WS_POPUP, 0, 0, 0, 0,
1840 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1841 (LPVOID)hmenu );
1842 if( !menu->hWnd ) return FALSE;
1843 return TRUE;
1847 /***********************************************************************
1848 * MENU_ShowPopup
1850 * Display a popup menu.
1852 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1853 INT x, INT y, INT xanchor, INT yanchor )
1855 POPUPMENU *menu;
1856 INT width, height;
1857 POINT pt;
1858 HMONITOR monitor;
1859 MONITORINFO info;
1861 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1862 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1864 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1865 if (menu->FocusedItem != NO_SELECTED_ITEM)
1867 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1868 menu->FocusedItem = NO_SELECTED_ITEM;
1871 menu->nScrollPos = 0;
1872 MENU_PopupMenuCalcSize( menu );
1874 /* adjust popup menu pos so that it fits within the desktop */
1876 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1877 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1879 /* FIXME: should use item rect */
1880 pt.x = x;
1881 pt.y = y;
1882 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1883 info.cbSize = sizeof(info);
1884 GetMonitorInfoW( monitor, &info );
1886 if (flags & TPM_LAYOUTRTL)
1887 flags ^= TPM_RIGHTALIGN;
1889 if( flags & TPM_RIGHTALIGN ) x -= width;
1890 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1892 if( flags & TPM_BOTTOMALIGN ) y -= height;
1893 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1895 if( x + width > info.rcWork.right)
1897 if( xanchor && x >= width - xanchor )
1898 x -= width - xanchor;
1900 if( x + width > info.rcWork.right)
1901 x = info.rcWork.right - width;
1903 if( x < info.rcWork.left ) x = info.rcWork.left;
1905 if( y + height > info.rcWork.bottom)
1907 if( yanchor && y >= height + yanchor )
1908 y -= height + yanchor;
1910 if( y + height > info.rcWork.bottom)
1911 y = info.rcWork.bottom - height;
1913 if( y < info.rcWork.top ) y = info.rcWork.top;
1915 if (!top_popup) {
1916 top_popup = menu->hWnd;
1917 top_popup_hmenu = hmenu;
1919 /* Display the window */
1921 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, width, height,
1922 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1923 UpdateWindow( menu->hWnd );
1924 return TRUE;
1928 /***********************************************************************
1929 * MENU_EnsureMenuItemVisible
1931 static void
1932 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1934 if (lppop->bScrolling)
1936 MENUITEM *item = &lppop->items[wIndex];
1937 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1938 UINT nOldPos = lppop->nScrollPos;
1939 RECT rc;
1940 UINT arrow_bitmap_height;
1941 BITMAP bmp;
1943 GetClientRect(lppop->hWnd, &rc);
1945 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1946 arrow_bitmap_height = bmp.bmHeight;
1948 rc.top += arrow_bitmap_height;
1949 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1951 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1952 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1955 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1956 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1957 MENU_DrawScrollArrows(lppop, hdc);
1959 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1961 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1962 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1963 MENU_DrawScrollArrows(lppop, hdc);
1969 /***********************************************************************
1970 * MENU_SelectItem
1972 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1973 BOOL sendMenuSelect, HMENU topmenu )
1975 LPPOPUPMENU lppop;
1976 HDC hdc;
1978 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1980 lppop = MENU_GetMenu( hmenu );
1981 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1983 if (lppop->FocusedItem == wIndex) return;
1984 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1985 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1986 if (!top_popup) {
1987 top_popup = lppop->hWnd;
1988 top_popup_hmenu = hmenu;
1991 SelectObject( hdc, get_menu_font(FALSE));
1993 /* Clear previous highlighted item */
1994 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1996 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1997 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1998 lppop->Height, !(lppop->wFlags & MF_POPUP),
1999 ODA_SELECT );
2002 /* Highlight new item (if any) */
2003 lppop->FocusedItem = wIndex;
2004 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2006 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2007 lppop->items[wIndex].fState |= MF_HILITE;
2008 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2009 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2010 &lppop->items[wIndex], lppop->Height,
2011 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2013 if (sendMenuSelect)
2015 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2016 SendMessageW( hwndOwner, WM_MENUSELECT,
2017 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2018 ip->fType | ip->fState |
2019 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2022 else if (sendMenuSelect) {
2023 if(topmenu){
2024 int pos;
2025 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2026 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2027 MENUITEM *ip = &ptm->items[pos];
2028 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2029 ip->fType | ip->fState |
2030 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2034 ReleaseDC( lppop->hWnd, hdc );
2038 /***********************************************************************
2039 * MENU_MoveSelection
2041 * Moves currently selected item according to the offset parameter.
2042 * If there is no selection then it should select the last item if
2043 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2045 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2047 INT i;
2048 POPUPMENU *menu;
2050 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2052 menu = MENU_GetMenu( hmenu );
2053 if ((!menu) || (!menu->items)) return;
2055 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2057 if( menu->nItems == 1 ) return; else
2058 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2059 ; i += offset)
2060 if (!(menu->items[i].fType & MF_SEPARATOR))
2062 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2063 return;
2067 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2068 i >= 0 && i < menu->nItems ; i += offset)
2069 if (!(menu->items[i].fType & MF_SEPARATOR))
2071 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2072 return;
2077 /**********************************************************************
2078 * MENU_InsertItem
2080 * Insert (allocate) a new item into a menu.
2082 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2084 MENUITEM *newItems;
2085 POPUPMENU *menu;
2087 if (!(menu = MENU_GetMenu(hMenu)))
2088 return NULL;
2090 /* Find where to insert new item */
2092 if (flags & MF_BYPOSITION) {
2093 if (pos > menu->nItems)
2094 pos = menu->nItems;
2095 } else {
2096 if (!MENU_FindItem( &hMenu, &pos, flags ))
2097 pos = menu->nItems;
2098 else {
2099 if (!(menu = MENU_GetMenu( hMenu )))
2100 return NULL;
2104 /* Make sure that MDI system buttons stay on the right side.
2105 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2106 * regardless of their id.
2108 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2109 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2110 pos--;
2112 TRACE("inserting at %u flags %x\n", pos, flags);
2114 /* Create new items array */
2116 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2117 if (!newItems)
2119 WARN("allocation failed\n" );
2120 return NULL;
2122 if (menu->nItems > 0)
2124 /* Copy the old array into the new one */
2125 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2126 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2127 (menu->nItems-pos)*sizeof(MENUITEM) );
2128 HeapFree( GetProcessHeap(), 0, menu->items );
2130 menu->items = newItems;
2131 menu->nItems++;
2132 memset( &newItems[pos], 0, sizeof(*newItems) );
2133 menu->Height = 0; /* force size recalculate */
2134 return &newItems[pos];
2138 /**********************************************************************
2139 * MENU_ParseResource
2141 * Parse a standard menu resource and add items to the menu.
2142 * Return a pointer to the end of the resource.
2144 * NOTE: flags is equivalent to the mtOption field
2146 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2148 WORD flags, id = 0;
2149 LPCWSTR str;
2150 BOOL end_flag;
2154 flags = GET_WORD(res);
2155 end_flag = flags & MF_END;
2156 /* Remove MF_END because it has the same value as MF_HILITE */
2157 flags &= ~MF_END;
2158 res += sizeof(WORD);
2159 if (!(flags & MF_POPUP))
2161 id = GET_WORD(res);
2162 res += sizeof(WORD);
2164 str = (LPCWSTR)res;
2165 res += (strlenW(str) + 1) * sizeof(WCHAR);
2166 if (flags & MF_POPUP)
2168 HMENU hSubMenu = CreatePopupMenu();
2169 if (!hSubMenu) return NULL;
2170 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2171 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2173 else /* Not a popup */
2175 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2177 } while (!end_flag);
2178 return res;
2182 /**********************************************************************
2183 * MENUEX_ParseResource
2185 * Parse an extended menu resource and add items to the menu.
2186 * Return a pointer to the end of the resource.
2188 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2190 WORD resinfo;
2191 do {
2192 MENUITEMINFOW mii;
2194 mii.cbSize = sizeof(mii);
2195 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2196 mii.fType = GET_DWORD(res);
2197 res += sizeof(DWORD);
2198 mii.fState = GET_DWORD(res);
2199 res += sizeof(DWORD);
2200 mii.wID = GET_DWORD(res);
2201 res += sizeof(DWORD);
2202 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2203 res += sizeof(WORD);
2204 /* Align the text on a word boundary. */
2205 res += (~((UINT_PTR)res - 1)) & 1;
2206 mii.dwTypeData = (LPWSTR) res;
2207 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2208 /* Align the following fields on a dword boundary. */
2209 res += (~((UINT_PTR)res - 1)) & 3;
2211 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2212 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2214 if (resinfo & 1) { /* Pop-up? */
2215 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2216 res += sizeof(DWORD);
2217 mii.hSubMenu = CreatePopupMenu();
2218 if (!mii.hSubMenu)
2219 return NULL;
2220 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2221 DestroyMenu(mii.hSubMenu);
2222 return NULL;
2224 mii.fMask |= MIIM_SUBMENU;
2225 mii.fType |= MF_POPUP;
2227 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2229 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2230 mii.wID, mii.fType);
2231 mii.fType |= MF_SEPARATOR;
2233 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2234 } while (!(resinfo & MF_END));
2235 return res;
2239 /***********************************************************************
2240 * MENU_GetSubPopup
2242 * Return the handle of the selected sub-popup menu (if any).
2244 static HMENU MENU_GetSubPopup( HMENU hmenu )
2246 POPUPMENU *menu;
2247 MENUITEM *item;
2249 menu = MENU_GetMenu( hmenu );
2251 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2253 item = &menu->items[menu->FocusedItem];
2254 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2255 return item->hSubMenu;
2256 return 0;
2260 /***********************************************************************
2261 * MENU_HideSubPopups
2263 * Hide the sub-popup menus of this menu.
2265 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2266 BOOL sendMenuSelect, UINT wFlags )
2268 POPUPMENU *menu = MENU_GetMenu( hmenu );
2270 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2272 if (menu && top_popup)
2274 HMENU hsubmenu;
2275 POPUPMENU *submenu;
2276 MENUITEM *item;
2278 if (menu->FocusedItem != NO_SELECTED_ITEM)
2280 item = &menu->items[menu->FocusedItem];
2281 if (!(item->fType & MF_POPUP) ||
2282 !(item->fState & MF_MOUSESELECT)) return;
2283 item->fState &= ~MF_MOUSESELECT;
2284 hsubmenu = item->hSubMenu;
2285 } else return;
2287 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2288 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2289 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2290 DestroyWindow( submenu->hWnd );
2291 submenu->hWnd = 0;
2293 if (!(wFlags & TPM_NONOTIFY))
2294 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2295 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2300 /***********************************************************************
2301 * MENU_ShowSubPopup
2303 * Display the sub-menu of the selected item of this menu.
2304 * Return the handle of the submenu, or hmenu if no submenu to display.
2306 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2307 BOOL selectFirst, UINT wFlags )
2309 RECT rect;
2310 POPUPMENU *menu;
2311 MENUITEM *item;
2312 HDC hdc;
2314 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2316 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2318 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2320 item = &menu->items[menu->FocusedItem];
2321 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2322 return hmenu;
2324 /* message must be sent before using item,
2325 because nearly everything may be changed by the application ! */
2327 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2328 if (!(wFlags & TPM_NONOTIFY))
2329 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2330 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2332 item = &menu->items[menu->FocusedItem];
2333 rect = item->rect;
2335 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2336 if (!(item->fState & MF_HILITE))
2338 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2339 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2341 SelectObject( hdc, get_menu_font(FALSE));
2343 item->fState |= MF_HILITE;
2344 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2345 ReleaseDC( menu->hWnd, hdc );
2347 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2348 item->rect = rect;
2350 item->fState |= MF_MOUSESELECT;
2352 if (IS_SYSTEM_MENU(menu))
2354 MENU_InitSysMenuPopup(item->hSubMenu,
2355 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2356 GetClassLongW( menu->hWnd, GCL_STYLE));
2358 NC_GetSysPopupPos( menu->hWnd, &rect );
2359 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2360 rect.top = rect.bottom;
2361 rect.right = GetSystemMetrics(SM_CXSIZE);
2362 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2364 else
2366 GetWindowRect( menu->hWnd, &rect );
2367 if (menu->wFlags & MF_POPUP)
2369 RECT rc = item->rect;
2371 MENU_AdjustMenuItemRect(menu, &rc);
2373 /* The first item in the popup menu has to be at the
2374 same y position as the focused menu item */
2375 if (wFlags & TPM_LAYOUTRTL)
2376 rect.left += GetSystemMetrics(SM_CXBORDER);
2377 else
2378 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2379 rect.top += rc.top - MENU_TOP_MARGIN;
2380 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2381 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2382 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2384 else
2386 if (wFlags & TPM_LAYOUTRTL)
2387 rect.left = rect.right - item->rect.left;
2388 else
2389 rect.left += item->rect.left;
2390 rect.top += item->rect.bottom;
2391 rect.right = item->rect.right - item->rect.left;
2392 rect.bottom = item->rect.bottom - item->rect.top;
2396 /* use default alignment for submenus */
2397 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2399 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2401 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2402 rect.left, rect.top, rect.right, rect.bottom );
2403 if (selectFirst)
2404 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2405 return item->hSubMenu;
2410 /**********************************************************************
2411 * MENU_IsMenuActive
2413 HWND MENU_IsMenuActive(void)
2415 return top_popup;
2418 /**********************************************************************
2419 * MENU_EndMenu
2421 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2423 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2425 void MENU_EndMenu( HWND hwnd )
2427 POPUPMENU *menu;
2428 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2429 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2432 /***********************************************************************
2433 * MENU_PtMenu
2435 * Walks menu chain trying to find a menu pt maps to.
2437 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2439 POPUPMENU *menu = MENU_GetMenu( hMenu );
2440 UINT item = menu->FocusedItem;
2441 HMENU ret;
2443 /* try subpopup first (if any) */
2444 ret = (item != NO_SELECTED_ITEM &&
2445 (menu->items[item].fType & MF_POPUP) &&
2446 (menu->items[item].fState & MF_MOUSESELECT))
2447 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2449 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2451 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2452 if( menu->wFlags & MF_POPUP )
2454 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2456 else if (ht == HTSYSMENU)
2457 ret = get_win_sys_menu( menu->hWnd );
2458 else if (ht == HTMENU)
2459 ret = GetMenu( menu->hWnd );
2461 return ret;
2464 /***********************************************************************
2465 * MENU_ExecFocusedItem
2467 * Execute a menu item (for instance when user pressed Enter).
2468 * Return the wID of the executed item. Otherwise, -1 indicating
2469 * that no menu item was executed, -2 if a popup is shown;
2470 * Have to receive the flags for the TrackPopupMenu options to avoid
2471 * sending unwanted message.
2474 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2476 MENUITEM *item;
2477 POPUPMENU *menu = MENU_GetMenu( hMenu );
2479 TRACE("%p hmenu=%p\n", pmt, hMenu);
2481 if (!menu || !menu->nItems ||
2482 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2484 item = &menu->items[menu->FocusedItem];
2486 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2488 if (!(item->fType & MF_POPUP))
2490 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2492 /* If TPM_RETURNCMD is set you return the id, but
2493 do not send a message to the owner */
2494 if(!(wFlags & TPM_RETURNCMD))
2496 if( menu->wFlags & MF_SYSMENU )
2497 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2498 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2499 else
2501 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2502 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2504 if (dwStyle & MNS_NOTIFYBYPOS)
2505 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2506 (LPARAM)hMenu);
2507 else
2508 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2511 return item->wID;
2514 else
2516 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2517 return -2;
2520 return -1;
2523 /***********************************************************************
2524 * MENU_SwitchTracking
2526 * Helper function for menu navigation routines.
2528 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2530 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2531 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2533 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2535 if( pmt->hTopMenu != hPtMenu &&
2536 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2538 /* both are top level menus (system and menu-bar) */
2539 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2540 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2541 pmt->hTopMenu = hPtMenu;
2543 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2544 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2548 /***********************************************************************
2549 * MENU_ButtonDown
2551 * Return TRUE if we can go on with menu tracking.
2553 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2555 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2557 if (hPtMenu)
2559 UINT id = 0;
2560 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2561 MENUITEM *item;
2563 if( IS_SYSTEM_MENU(ptmenu) )
2564 item = ptmenu->items;
2565 else
2566 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2568 if( item )
2570 if( ptmenu->FocusedItem != id )
2571 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2573 /* If the popup menu is not already "popped" */
2574 if(!(item->fState & MF_MOUSESELECT ))
2576 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2579 return TRUE;
2581 /* Else the click was on the menu bar, finish the tracking */
2583 return FALSE;
2586 /***********************************************************************
2587 * MENU_ButtonUp
2589 * Return the value of MENU_ExecFocusedItem if
2590 * the selected item was not a popup. Else open the popup.
2591 * A -1 return value indicates that we go on with menu tracking.
2594 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2596 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2598 if (hPtMenu)
2600 UINT id = 0;
2601 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2602 MENUITEM *item;
2604 if( IS_SYSTEM_MENU(ptmenu) )
2605 item = ptmenu->items;
2606 else
2607 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2609 if( item && (ptmenu->FocusedItem == id ))
2611 debug_print_menuitem ("FocusedItem: ", item, "");
2613 if( !(item->fType & MF_POPUP) )
2615 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2616 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2617 return executedMenuId;
2620 /* If we are dealing with the menu bar */
2621 /* and this is a click on an already "popped" item: */
2622 /* Stop the menu tracking and close the opened submenus */
2623 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2624 return 0;
2626 if( GetMenu(ptmenu->hWnd) == hPtMenu )
2627 ptmenu->bTimeToHide = TRUE;
2629 return -1;
2633 /***********************************************************************
2634 * MENU_MouseMove
2636 * Return TRUE if we can go on with menu tracking.
2638 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2640 UINT id = NO_SELECTED_ITEM;
2641 POPUPMENU *ptmenu = NULL;
2643 if( hPtMenu )
2645 ptmenu = MENU_GetMenu( hPtMenu );
2646 if( IS_SYSTEM_MENU(ptmenu) )
2647 id = 0;
2648 else
2649 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2652 if( id == NO_SELECTED_ITEM )
2654 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2655 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2658 else if( ptmenu->FocusedItem != id )
2660 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2661 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2663 return TRUE;
2667 /***********************************************************************
2668 * MENU_DoNextMenu
2670 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2672 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2674 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2675 BOOL atEnd = FALSE;
2677 /* When skipping left, we need to do something special after the
2678 first menu. */
2679 if (vk == VK_LEFT && menu->FocusedItem == 0)
2681 atEnd = TRUE;
2683 /* When skipping right, for the non-system menu, we need to
2684 handle the last non-special menu item (ie skip any window
2685 icons such as MDI maximize, restore or close) */
2686 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2688 UINT i = menu->FocusedItem + 1;
2689 while (i < menu->nItems) {
2690 if ((menu->items[i].wID >= SC_SIZE &&
2691 menu->items[i].wID <= SC_RESTORE)) {
2692 i++;
2693 } else break;
2695 if (i == menu->nItems) {
2696 atEnd = TRUE;
2699 /* When skipping right, we need to cater for the system menu */
2700 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2702 if (menu->FocusedItem == (menu->nItems - 1)) {
2703 atEnd = TRUE;
2707 if( atEnd )
2709 MDINEXTMENU next_menu;
2710 HMENU hNewMenu;
2711 HWND hNewWnd;
2712 UINT id = 0;
2714 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2715 next_menu.hmenuNext = 0;
2716 next_menu.hwndNext = 0;
2717 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2719 TRACE("%p [%p] -> %p [%p]\n",
2720 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2722 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2724 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2725 hNewWnd = pmt->hOwnerWnd;
2726 if( IS_SYSTEM_MENU(menu) )
2728 /* switch to the menu bar */
2730 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2732 if( vk == VK_LEFT )
2734 menu = MENU_GetMenu( hNewMenu );
2735 id = menu->nItems - 1;
2737 /* Skip backwards over any system predefined icons,
2738 eg. MDI close, restore etc icons */
2739 while ((id > 0) &&
2740 (menu->items[id].wID >= SC_SIZE &&
2741 menu->items[id].wID <= SC_RESTORE)) id--;
2744 else if (style & WS_SYSMENU )
2746 /* switch to the system menu */
2747 hNewMenu = get_win_sys_menu( hNewWnd );
2749 else return FALSE;
2751 else /* application returned a new menu to switch to */
2753 hNewMenu = next_menu.hmenuNext;
2754 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2756 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2758 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2760 if (style & WS_SYSMENU &&
2761 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2763 /* get the real system menu */
2764 hNewMenu = get_win_sys_menu(hNewWnd);
2766 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2768 /* FIXME: Not sure what to do here;
2769 * perhaps try to track hNewMenu as a popup? */
2771 TRACE(" -- got confused.\n");
2772 return FALSE;
2775 else return FALSE;
2778 if( hNewMenu != pmt->hTopMenu )
2780 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2781 FALSE, 0 );
2782 if( pmt->hCurrentMenu != pmt->hTopMenu )
2783 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2786 if( hNewWnd != pmt->hOwnerWnd )
2788 pmt->hOwnerWnd = hNewWnd;
2789 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2792 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2793 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2795 return TRUE;
2797 return FALSE;
2800 /***********************************************************************
2801 * MENU_SuspendPopup
2803 * The idea is not to show the popup if the next input message is
2804 * going to hide it anyway.
2806 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2808 MSG msg;
2810 msg.hwnd = pmt->hOwnerWnd;
2812 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2813 pmt->trackFlags |= TF_SKIPREMOVE;
2815 switch( uMsg )
2817 case WM_KEYDOWN:
2818 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2819 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2821 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2822 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2823 if( msg.message == WM_KEYDOWN &&
2824 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2826 pmt->trackFlags |= TF_SUSPENDPOPUP;
2827 return TRUE;
2830 break;
2833 /* failures go through this */
2834 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2835 return FALSE;
2838 /***********************************************************************
2839 * MENU_KeyEscape
2841 * Handle a VK_ESCAPE key event in a menu.
2843 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2845 BOOL bEndMenu = TRUE;
2847 if (pmt->hCurrentMenu != pmt->hTopMenu)
2849 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2851 if (menu->wFlags & MF_POPUP)
2853 HMENU hmenutmp, hmenuprev;
2855 hmenuprev = hmenutmp = pmt->hTopMenu;
2857 /* close topmost popup */
2858 while (hmenutmp != pmt->hCurrentMenu)
2860 hmenuprev = hmenutmp;
2861 hmenutmp = MENU_GetSubPopup( hmenuprev );
2864 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2865 pmt->hCurrentMenu = hmenuprev;
2866 bEndMenu = FALSE;
2870 return bEndMenu;
2873 /***********************************************************************
2874 * MENU_KeyLeft
2876 * Handle a VK_LEFT key event in a menu.
2878 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2880 POPUPMENU *menu;
2881 HMENU hmenutmp, hmenuprev;
2882 UINT prevcol;
2884 hmenuprev = hmenutmp = pmt->hTopMenu;
2885 menu = MENU_GetMenu( hmenutmp );
2887 /* Try to move 1 column left (if possible) */
2888 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2889 NO_SELECTED_ITEM ) {
2891 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2892 prevcol, TRUE, 0 );
2893 return;
2896 /* close topmost popup */
2897 while (hmenutmp != pmt->hCurrentMenu)
2899 hmenuprev = hmenutmp;
2900 hmenutmp = MENU_GetSubPopup( hmenuprev );
2903 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2904 pmt->hCurrentMenu = hmenuprev;
2906 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2908 /* move menu bar selection if no more popups are left */
2910 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2911 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2913 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2915 /* A sublevel menu was displayed - display the next one
2916 * unless there is another displacement coming up */
2918 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2919 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2920 pmt->hTopMenu, TRUE, wFlags);
2926 /***********************************************************************
2927 * MENU_KeyRight
2929 * Handle a VK_RIGHT key event in a menu.
2931 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2933 HMENU hmenutmp;
2934 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2935 UINT nextcol;
2937 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2938 pmt->hCurrentMenu,
2939 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2940 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2942 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2944 /* If already displaying a popup, try to display sub-popup */
2946 hmenutmp = pmt->hCurrentMenu;
2947 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2949 /* if subpopup was displayed then we are done */
2950 if (hmenutmp != pmt->hCurrentMenu) return;
2953 /* Check to see if there's another column */
2954 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2955 NO_SELECTED_ITEM ) {
2956 TRACE("Going to %d.\n", nextcol );
2957 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2958 nextcol, TRUE, 0 );
2959 return;
2962 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2964 if( pmt->hCurrentMenu != pmt->hTopMenu )
2966 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2967 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2968 } else hmenutmp = 0;
2970 /* try to move to the next item */
2971 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2972 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2974 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2975 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2976 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2977 pmt->hTopMenu, TRUE, wFlags);
2981 static void CALLBACK release_capture( BOOL __normal )
2983 set_capture_window( 0, GUI_INMENUMODE, NULL );
2986 /***********************************************************************
2987 * MENU_TrackMenu
2989 * Menu tracking code.
2991 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2992 HWND hwnd, const RECT *lprect )
2994 MSG msg;
2995 POPUPMENU *menu;
2996 BOOL fRemove;
2997 INT executedMenuId = -1;
2998 MTRACKER mt;
2999 BOOL enterIdleSent = FALSE;
3000 HWND capture_win;
3002 mt.trackFlags = 0;
3003 mt.hCurrentMenu = hmenu;
3004 mt.hTopMenu = hmenu;
3005 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3006 mt.pt.x = x;
3007 mt.pt.y = y;
3009 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3010 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3012 if (!(menu = MENU_GetMenu( hmenu )))
3014 WARN("Invalid menu handle %p\n", hmenu);
3015 SetLastError(ERROR_INVALID_MENU_HANDLE);
3016 return FALSE;
3019 if (wFlags & TPM_BUTTONDOWN)
3021 /* Get the result in order to start the tracking or not */
3022 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3023 fEndMenu = !fRemove;
3026 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3028 /* owner may not be visible when tracking a popup, so use the menu itself */
3029 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3030 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3032 __TRY while (!fEndMenu)
3034 menu = MENU_GetMenu( mt.hCurrentMenu );
3035 if (!menu) /* sometimes happens if I do a window manager close */
3036 break;
3038 /* we have to keep the message in the queue until it's
3039 * clear that menu loop is not over yet. */
3041 for (;;)
3043 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3045 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3046 /* remove the message from the queue */
3047 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3049 else
3051 if (!enterIdleSent)
3053 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3054 enterIdleSent = TRUE;
3055 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3057 WaitMessage();
3061 /* check if EndMenu() tried to cancel us, by posting this message */
3062 if(msg.message == WM_CANCELMODE)
3064 /* we are now out of the loop */
3065 fEndMenu = TRUE;
3067 /* remove the message from the queue */
3068 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3070 /* break out of internal loop, ala ESCAPE */
3071 break;
3074 TranslateMessage( &msg );
3075 mt.pt = msg.pt;
3077 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3078 enterIdleSent=FALSE;
3080 fRemove = FALSE;
3081 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3084 * Use the mouse coordinates in lParam instead of those in the MSG
3085 * struct to properly handle synthetic messages. They are already
3086 * in screen coordinates.
3088 mt.pt.x = (short)LOWORD(msg.lParam);
3089 mt.pt.y = (short)HIWORD(msg.lParam);
3091 /* Find a menu for this mouse event */
3092 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3094 switch(msg.message)
3096 /* no WM_NC... messages in captured state */
3098 case WM_RBUTTONDBLCLK:
3099 case WM_RBUTTONDOWN:
3100 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3101 /* fall through */
3102 case WM_LBUTTONDBLCLK:
3103 case WM_LBUTTONDOWN:
3104 /* If the message belongs to the menu, removes it from the queue */
3105 /* Else, end menu tracking */
3106 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3107 fEndMenu = !fRemove;
3108 break;
3110 case WM_RBUTTONUP:
3111 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3112 /* fall through */
3113 case WM_LBUTTONUP:
3114 /* Check if a menu was selected by the mouse */
3115 if (hmenu)
3117 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3118 TRACE("executedMenuId %d\n", executedMenuId);
3120 /* End the loop if executedMenuId is an item ID */
3121 /* or if the job was done (executedMenuId = 0). */
3122 fEndMenu = fRemove = (executedMenuId != -1);
3124 /* No menu was selected by the mouse */
3125 /* if the function was called by TrackPopupMenu, continue
3126 with the menu tracking. If not, stop it */
3127 else
3128 fEndMenu = !(wFlags & TPM_POPUPMENU);
3130 break;
3132 case WM_MOUSEMOVE:
3133 /* the selected menu item must be changed every time */
3134 /* the mouse moves. */
3136 if (hmenu)
3137 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3139 } /* switch(msg.message) - mouse */
3141 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3143 fRemove = TRUE; /* Keyboard messages are always removed */
3144 switch(msg.message)
3146 case WM_KEYDOWN:
3147 case WM_SYSKEYDOWN:
3148 switch(msg.wParam)
3150 case VK_MENU:
3151 case VK_F10:
3152 fEndMenu = TRUE;
3153 break;
3155 case VK_HOME:
3156 case VK_END:
3157 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3158 NO_SELECTED_ITEM, FALSE, 0 );
3159 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3160 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3161 break;
3163 case VK_UP:
3164 case VK_DOWN: /* If on menu bar, pull-down the menu */
3166 menu = MENU_GetMenu( mt.hCurrentMenu );
3167 if (!(menu->wFlags & MF_POPUP))
3168 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3169 else /* otherwise try to move selection */
3170 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3171 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3172 break;
3174 case VK_LEFT:
3175 MENU_KeyLeft( &mt, wFlags );
3176 break;
3178 case VK_RIGHT:
3179 MENU_KeyRight( &mt, wFlags );
3180 break;
3182 case VK_ESCAPE:
3183 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3184 break;
3186 case VK_F1:
3188 HELPINFO hi;
3189 hi.cbSize = sizeof(HELPINFO);
3190 hi.iContextType = HELPINFO_MENUITEM;
3191 if (menu->FocusedItem == NO_SELECTED_ITEM)
3192 hi.iCtrlId = 0;
3193 else
3194 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3195 hi.hItemHandle = hmenu;
3196 hi.dwContextId = menu->dwContextHelpID;
3197 hi.MousePos = msg.pt;
3198 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3199 break;
3202 default:
3203 break;
3205 break; /* WM_KEYDOWN */
3207 case WM_CHAR:
3208 case WM_SYSCHAR:
3210 UINT pos;
3212 if (msg.wParam == '\r' || msg.wParam == ' ')
3214 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3215 fEndMenu = (executedMenuId != -2);
3217 break;
3220 /* Hack to avoid control chars. */
3221 /* We will find a better way real soon... */
3222 if (msg.wParam < 32) break;
3224 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3225 LOWORD(msg.wParam), FALSE );
3226 if (pos == (UINT)-2) fEndMenu = TRUE;
3227 else if (pos == (UINT)-1) MessageBeep(0);
3228 else
3230 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3231 TRUE, 0 );
3232 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3233 fEndMenu = (executedMenuId != -2);
3236 break;
3237 } /* switch(msg.message) - kbd */
3239 else
3241 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3242 DispatchMessageW( &msg );
3243 continue;
3246 if (!fEndMenu) fRemove = TRUE;
3248 /* finally remove message from the queue */
3250 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3251 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3252 else mt.trackFlags &= ~TF_SKIPREMOVE;
3254 __FINALLY( release_capture )
3256 /* If dropdown is still painted and the close box is clicked on
3257 then the menu will be destroyed as part of the DispatchMessage above.
3258 This will then invalidate the menu handle in mt.hTopMenu. We should
3259 check for this first. */
3260 if( IsMenu( mt.hTopMenu ) )
3262 menu = MENU_GetMenu( mt.hTopMenu );
3264 if( IsWindow( mt.hOwnerWnd ) )
3266 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3268 if (menu && (menu->wFlags & MF_POPUP))
3270 DestroyWindow( menu->hWnd );
3271 menu->hWnd = 0;
3273 if (!(wFlags & TPM_NONOTIFY))
3274 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3275 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3277 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3278 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3281 /* Reset the variable for hiding menu */
3282 if( menu ) menu->bTimeToHide = FALSE;
3285 /* The return value is only used by TrackPopupMenu */
3286 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3287 if (executedMenuId == -1) executedMenuId = 0;
3288 return executedMenuId;
3291 /***********************************************************************
3292 * MENU_InitTracking
3294 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3296 POPUPMENU *menu;
3298 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3300 HideCaret(0);
3302 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3304 /* This makes the menus of applications built with Delphi work.
3305 * It also enables menus to be displayed in more than one window,
3306 * but there are some bugs left that need to be fixed in this case.
3308 if (!bPopup) menu->hWnd = hWnd;
3309 if (!top_popup)
3311 top_popup = menu->hWnd;
3312 top_popup_hmenu = hMenu;
3315 fEndMenu = FALSE;
3317 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3318 if (!(wFlags & TPM_NONOTIFY))
3319 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3321 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3323 if (!(wFlags & TPM_NONOTIFY))
3325 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3326 /* If an app changed/recreated menu bar entries in WM_INITMENU
3327 * menu sizes will be recalculated once the menu created/shown.
3331 return TRUE;
3334 /***********************************************************************
3335 * MENU_ExitTracking
3337 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3339 TRACE("hwnd=%p\n", hWnd);
3341 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3342 ShowCaret(0);
3343 top_popup = 0;
3344 top_popup_hmenu = NULL;
3345 return TRUE;
3348 /***********************************************************************
3349 * MENU_TrackMouseMenuBar
3351 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3353 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3355 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3356 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3358 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3360 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3361 if (IsMenu(hMenu))
3363 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3365 /* fetch the window menu again, it may have changed */
3366 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3367 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3368 MENU_ExitTracking(hWnd, FALSE);
3373 /***********************************************************************
3374 * MENU_TrackKbdMenuBar
3376 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3378 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3380 UINT uItem = NO_SELECTED_ITEM;
3381 HMENU hTrackMenu;
3382 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3384 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3386 /* find window that has a menu */
3388 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3389 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3391 /* check if we have to track a system menu */
3393 hTrackMenu = GetMenu( hwnd );
3394 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3396 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3397 hTrackMenu = get_win_sys_menu( hwnd );
3398 uItem = 0;
3399 wParam |= HTSYSMENU; /* prevent item lookup */
3401 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3403 if (!IsMenu( hTrackMenu )) return;
3405 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3407 /* fetch the window menu again, it may have changed */
3408 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3410 if( wChar && wChar != ' ' )
3412 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3413 if ( uItem >= (UINT)(-2) )
3415 if( uItem == (UINT)(-1) ) MessageBeep(0);
3416 /* schedule end of menu tracking */
3417 wFlags |= TF_ENDMENU;
3418 goto track_menu;
3422 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3424 if (!(wParam & HTSYSMENU) || wChar == ' ')
3426 if( uItem == NO_SELECTED_ITEM )
3427 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3428 else
3429 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3432 track_menu:
3433 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3434 MENU_ExitTracking( hwnd, FALSE );
3437 /**********************************************************************
3438 * TrackPopupMenuEx (USER32.@)
3440 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3441 HWND hWnd, LPTPMPARAMS lpTpm )
3443 POPUPMENU *menu;
3444 BOOL ret = FALSE;
3446 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3447 hMenu, wFlags, x, y, hWnd, lpTpm,
3448 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3450 /* Parameter check */
3451 /* FIXME: this check is performed several times, here and in the called
3452 functions. That could be optimized */
3453 if (!(menu = MENU_GetMenu( hMenu )))
3455 SetLastError( ERROR_INVALID_MENU_HANDLE );
3456 return FALSE;
3459 if (IsWindow(menu->hWnd))
3461 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3462 return FALSE;
3465 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3467 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3469 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3470 if (!(wFlags & TPM_NONOTIFY))
3471 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3473 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3474 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3475 lpTpm ? &lpTpm->rcExclude : NULL );
3476 MENU_ExitTracking(hWnd, TRUE);
3478 if (menu->hWnd)
3480 DestroyWindow( menu->hWnd );
3481 menu->hWnd = 0;
3483 if (!(wFlags & TPM_NONOTIFY))
3484 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3485 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3489 return ret;
3492 /**********************************************************************
3493 * TrackPopupMenu (USER32.@)
3495 * Like the win32 API, the function return the command ID only if the
3496 * flag TPM_RETURNCMD is on.
3499 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3500 INT nReserved, HWND hWnd, const RECT *lpRect )
3502 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3505 /***********************************************************************
3506 * PopupMenuWndProc
3508 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3510 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3512 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3514 switch(message)
3516 case WM_CREATE:
3518 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3519 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3520 return 0;
3523 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3524 return MA_NOACTIVATE;
3526 case WM_PAINT:
3528 PAINTSTRUCT ps;
3529 BeginPaint( hwnd, &ps );
3530 MENU_DrawPopupMenu( hwnd, ps.hdc,
3531 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3532 EndPaint( hwnd, &ps );
3533 return 0;
3536 case WM_PRINTCLIENT:
3538 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3539 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3540 return 0;
3543 case WM_ERASEBKGND:
3544 return 1;
3546 case WM_DESTROY:
3547 /* zero out global pointer in case resident popup window was destroyed. */
3548 if (hwnd == top_popup) {
3549 top_popup = 0;
3550 top_popup_hmenu = NULL;
3552 break;
3554 case WM_SHOWWINDOW:
3556 if( wParam )
3558 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3560 else
3561 SetWindowLongPtrW( hwnd, 0, 0 );
3562 break;
3564 case MN_GETHMENU:
3565 return GetWindowLongPtrW( hwnd, 0 );
3567 default:
3568 return DefWindowProcW( hwnd, message, wParam, lParam );
3570 return 0;
3574 /***********************************************************************
3575 * MENU_GetMenuBarHeight
3577 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3579 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3580 INT orgX, INT orgY )
3582 HDC hdc;
3583 RECT rectBar;
3584 LPPOPUPMENU lppop;
3586 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3588 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3590 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3591 SelectObject( hdc, get_menu_font(FALSE));
3592 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3593 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3594 ReleaseDC( hwnd, hdc );
3595 return lppop->Height;
3599 /*******************************************************************
3600 * ChangeMenuA (USER32.@)
3602 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3603 UINT id, UINT flags )
3605 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3606 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3607 id, data );
3608 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3609 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3610 id, data );
3611 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3612 flags & MF_BYPOSITION ? pos : id,
3613 flags & ~MF_REMOVE );
3614 /* Default: MF_INSERT */
3615 return InsertMenuA( hMenu, pos, flags, id, data );
3619 /*******************************************************************
3620 * ChangeMenuW (USER32.@)
3622 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3623 UINT id, UINT flags )
3625 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3626 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3627 id, data );
3628 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3629 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3630 id, data );
3631 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3632 flags & MF_BYPOSITION ? pos : id,
3633 flags & ~MF_REMOVE );
3634 /* Default: MF_INSERT */
3635 return InsertMenuW( hMenu, pos, flags, id, data );
3639 /*******************************************************************
3640 * CheckMenuItem (USER32.@)
3642 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3644 MENUITEM *item;
3645 DWORD ret;
3647 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3648 ret = item->fState & MF_CHECKED;
3649 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3650 else item->fState &= ~MF_CHECKED;
3651 return ret;
3655 /**********************************************************************
3656 * EnableMenuItem (USER32.@)
3658 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3660 UINT oldflags;
3661 MENUITEM *item;
3662 POPUPMENU *menu;
3664 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3666 /* Get the Popupmenu to access the owner menu */
3667 if (!(menu = MENU_GetMenu(hMenu)))
3668 return (UINT)-1;
3670 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3671 return (UINT)-1;
3673 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3674 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3676 /* If the close item in the system menu change update the close button */
3677 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3679 if (menu->hSysMenuOwner != 0)
3681 RECT rc;
3682 POPUPMENU* parentMenu;
3684 /* Get the parent menu to access*/
3685 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3686 return (UINT)-1;
3688 /* Refresh the frame to reflect the change */
3689 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3690 rc.bottom = 0;
3691 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3695 return oldflags;
3699 /*******************************************************************
3700 * GetMenuStringA (USER32.@)
3702 INT WINAPI GetMenuStringA(
3703 HMENU hMenu, /* [in] menuhandle */
3704 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3705 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3706 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3707 UINT wFlags /* [in] MF_ flags */
3709 MENUITEM *item;
3711 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3712 if (str && nMaxSiz) str[0] = '\0';
3713 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3714 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3715 return 0;
3717 if (!item->text) return 0;
3718 if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3719 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3720 str[nMaxSiz-1] = 0;
3721 TRACE("returning %s\n", debugstr_a(str));
3722 return strlen(str);
3726 /*******************************************************************
3727 * GetMenuStringW (USER32.@)
3729 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3730 LPWSTR str, INT nMaxSiz, UINT wFlags )
3732 MENUITEM *item;
3734 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3735 if (str && nMaxSiz) str[0] = '\0';
3736 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3737 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3738 return 0;
3740 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3741 if( !(item->text)) {
3742 str[0] = 0;
3743 return 0;
3745 lstrcpynW( str, item->text, nMaxSiz );
3746 TRACE("returning %s\n", debugstr_w(str));
3747 return strlenW(str);
3751 /**********************************************************************
3752 * HiliteMenuItem (USER32.@)
3754 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3755 UINT wHilite )
3757 LPPOPUPMENU menu;
3758 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3759 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3760 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3761 if (menu->FocusedItem == wItemID) return TRUE;
3762 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3763 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3764 return TRUE;
3768 /**********************************************************************
3769 * GetMenuState (USER32.@)
3771 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3773 MENUITEM *item;
3774 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3775 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3776 debug_print_menuitem (" item: ", item, "");
3777 if (item->fType & MF_POPUP)
3779 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3780 if (!menu) return -1;
3781 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3783 else
3785 /* We used to (from way back then) mask the result to 0xff. */
3786 /* I don't know why and it seems wrong as the documented */
3787 /* return flag MF_SEPARATOR is outside that mask. */
3788 return (item->fType | item->fState);
3793 /**********************************************************************
3794 * GetMenuItemCount (USER32.@)
3796 INT WINAPI GetMenuItemCount( HMENU hMenu )
3798 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3799 if (!menu) return -1;
3800 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3801 return menu->nItems;
3805 /**********************************************************************
3806 * GetMenuItemID (USER32.@)
3808 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3810 MENUITEM * lpmi;
3812 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3813 if (lpmi->fType & MF_POPUP) return -1;
3814 return lpmi->wID;
3819 /**********************************************************************
3820 * MENU_mnu2mnuii
3822 * Uses flags, id and text ptr, passed by InsertMenu() and
3823 * ModifyMenu() to setup a MenuItemInfo structure.
3825 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3826 LPMENUITEMINFOW pmii)
3828 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3829 pmii->cbSize = sizeof( MENUITEMINFOW);
3830 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3831 /* setting bitmap clears text and vice versa */
3832 if( IS_STRING_ITEM(flags)) {
3833 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3834 if( !str)
3835 flags |= MF_SEPARATOR;
3836 /* Item beginning with a backspace is a help item */
3837 /* FIXME: wrong place, this is only true in win16 */
3838 else if( *str == '\b') {
3839 flags |= MF_HELP;
3840 str++;
3842 pmii->dwTypeData = (LPWSTR)str;
3843 } else if( flags & MFT_BITMAP){
3844 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3845 pmii->hbmpItem = (HBITMAP)str;
3847 if( flags & MF_OWNERDRAW){
3848 pmii->fMask |= MIIM_DATA;
3849 pmii->dwItemData = (ULONG_PTR) str;
3851 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3852 pmii->fMask |= MIIM_SUBMENU;
3853 pmii->hSubMenu = (HMENU)id;
3855 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3856 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3857 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3858 pmii->wID = (UINT)id;
3862 /*******************************************************************
3863 * InsertMenuW (USER32.@)
3865 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3866 UINT_PTR id, LPCWSTR str )
3868 MENUITEM *item;
3869 MENUITEMINFOW mii;
3871 if (IS_STRING_ITEM(flags) && str)
3872 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3873 hMenu, pos, flags, id, debugstr_w(str) );
3874 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3875 hMenu, pos, flags, id, str );
3877 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3878 MENU_mnu2mnuii( flags, id, str, &mii);
3879 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3881 RemoveMenu( hMenu, pos, flags );
3882 return FALSE;
3885 item->hCheckBit = item->hUnCheckBit = 0;
3886 return TRUE;
3890 /*******************************************************************
3891 * InsertMenuA (USER32.@)
3893 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3894 UINT_PTR id, LPCSTR str )
3896 BOOL ret = FALSE;
3898 if (IS_STRING_ITEM(flags) && str)
3900 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3901 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3902 if (newstr)
3904 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3905 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3906 HeapFree( GetProcessHeap(), 0, newstr );
3908 return ret;
3910 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3914 /*******************************************************************
3915 * AppendMenuA (USER32.@)
3917 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3918 UINT_PTR id, LPCSTR data )
3920 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3924 /*******************************************************************
3925 * AppendMenuW (USER32.@)
3927 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3928 UINT_PTR id, LPCWSTR data )
3930 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3934 /**********************************************************************
3935 * RemoveMenu (USER32.@)
3937 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3939 LPPOPUPMENU menu;
3940 MENUITEM *item;
3942 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3943 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3944 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3946 /* Remove item */
3948 MENU_FreeItemData( item );
3950 if (--menu->nItems == 0)
3952 HeapFree( GetProcessHeap(), 0, menu->items );
3953 menu->items = NULL;
3955 else
3957 while(nPos < menu->nItems)
3959 *item = *(item+1);
3960 item++;
3961 nPos++;
3963 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3964 menu->nItems * sizeof(MENUITEM) );
3966 return TRUE;
3970 /**********************************************************************
3971 * DeleteMenu (USER32.@)
3973 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3975 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3976 if (!item) return FALSE;
3977 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3978 /* nPos is now the position of the item */
3979 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3980 return TRUE;
3984 /*******************************************************************
3985 * ModifyMenuW (USER32.@)
3987 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3988 UINT_PTR id, LPCWSTR str )
3990 MENUITEM *item;
3991 MENUITEMINFOW mii;
3993 if (IS_STRING_ITEM(flags))
3994 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3995 else
3996 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3998 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3999 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4000 MENU_mnu2mnuii( flags, id, str, &mii);
4001 return SetMenuItemInfo_common( item, &mii, TRUE);
4005 /*******************************************************************
4006 * ModifyMenuA (USER32.@)
4008 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4009 UINT_PTR id, LPCSTR str )
4011 BOOL ret = FALSE;
4013 if (IS_STRING_ITEM(flags) && str)
4015 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4016 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4017 if (newstr)
4019 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4020 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4021 HeapFree( GetProcessHeap(), 0, newstr );
4023 return ret;
4025 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4029 /**********************************************************************
4030 * CreatePopupMenu (USER32.@)
4032 HMENU WINAPI CreatePopupMenu(void)
4034 HMENU hmenu;
4035 POPUPMENU *menu;
4037 if (!(hmenu = CreateMenu())) return 0;
4038 menu = MENU_GetMenu( hmenu );
4039 menu->wFlags |= MF_POPUP;
4040 menu->bTimeToHide = FALSE;
4041 return hmenu;
4045 /**********************************************************************
4046 * GetMenuCheckMarkDimensions (USER.417)
4047 * GetMenuCheckMarkDimensions (USER32.@)
4049 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4051 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4055 /**********************************************************************
4056 * SetMenuItemBitmaps (USER32.@)
4058 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4059 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4061 MENUITEM *item;
4063 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4065 if (!hNewCheck && !hNewUnCheck)
4067 item->fState &= ~MF_USECHECKBITMAPS;
4069 else /* Install new bitmaps */
4071 item->hCheckBit = hNewCheck;
4072 item->hUnCheckBit = hNewUnCheck;
4073 item->fState |= MF_USECHECKBITMAPS;
4075 return TRUE;
4079 /**********************************************************************
4080 * CreateMenu (USER32.@)
4082 HMENU WINAPI CreateMenu(void)
4084 HMENU hMenu;
4085 LPPOPUPMENU menu;
4087 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4088 menu->FocusedItem = NO_SELECTED_ITEM;
4089 menu->bTimeToHide = FALSE;
4091 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4093 TRACE("return %p\n", hMenu );
4095 return hMenu;
4099 /**********************************************************************
4100 * DestroyMenu (USER32.@)
4102 BOOL WINAPI DestroyMenu( HMENU hMenu )
4104 LPPOPUPMENU lppop;
4106 TRACE("(%p)\n", hMenu);
4108 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4109 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4111 /* DestroyMenu should not destroy system menu popup owner */
4112 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4114 DestroyWindow( lppop->hWnd );
4115 lppop->hWnd = 0;
4118 if (lppop->items) /* recursively destroy submenus */
4120 int i;
4121 MENUITEM *item = lppop->items;
4122 for (i = lppop->nItems; i > 0; i--, item++)
4124 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4125 MENU_FreeItemData( item );
4127 HeapFree( GetProcessHeap(), 0, lppop->items );
4129 HeapFree( GetProcessHeap(), 0, lppop );
4130 return TRUE;
4134 /**********************************************************************
4135 * GetSystemMenu (USER32.@)
4137 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4139 WND *wndPtr = WIN_GetPtr( hWnd );
4140 HMENU retvalue = 0;
4142 if (wndPtr == WND_DESKTOP) return 0;
4143 if (wndPtr == WND_OTHER_PROCESS)
4145 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4147 else if (wndPtr)
4149 if (wndPtr->hSysMenu && bRevert)
4151 DestroyMenu(wndPtr->hSysMenu);
4152 wndPtr->hSysMenu = 0;
4155 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4156 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4158 if( wndPtr->hSysMenu )
4160 POPUPMENU *menu;
4161 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4163 /* Store the dummy sysmenu handle to facilitate the refresh */
4164 /* of the close button if the SC_CLOSE item change */
4165 menu = MENU_GetMenu(retvalue);
4166 if ( menu )
4167 menu->hSysMenuOwner = wndPtr->hSysMenu;
4169 WIN_ReleasePtr( wndPtr );
4171 return bRevert ? 0 : retvalue;
4175 /*******************************************************************
4176 * SetSystemMenu (USER32.@)
4178 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4180 WND *wndPtr = WIN_GetPtr( hwnd );
4182 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4184 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4185 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4186 WIN_ReleasePtr( wndPtr );
4187 return TRUE;
4189 return FALSE;
4193 /**********************************************************************
4194 * GetMenu (USER32.@)
4196 HMENU WINAPI GetMenu( HWND hWnd )
4198 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4199 TRACE("for %p returning %p\n", hWnd, retvalue);
4200 return retvalue;
4203 /**********************************************************************
4204 * GetMenuBarInfo (USER32.@)
4206 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4208 POPUPMENU *menu;
4209 HMENU hmenu = NULL;
4210 ATOM class_atom;
4212 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4214 switch (idObject)
4216 case OBJID_CLIENT:
4217 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4218 if (!class_atom)
4219 return FALSE;
4220 if (class_atom != POPUPMENU_CLASS_ATOM)
4222 WARN("called on invalid window: %d\n", class_atom);
4223 SetLastError(ERROR_INVALID_MENU_HANDLE);
4224 return FALSE;
4227 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4228 break;
4229 case OBJID_MENU:
4230 hmenu = GetMenu(hwnd);
4231 break;
4232 case OBJID_SYSMENU:
4233 hmenu = GetSystemMenu(hwnd, FALSE);
4234 break;
4235 default:
4236 return FALSE;
4239 if (!hmenu)
4240 return FALSE;
4242 if (pmbi->cbSize != sizeof(MENUBARINFO))
4244 SetLastError(ERROR_INVALID_PARAMETER);
4245 return FALSE;
4248 menu = MENU_GetMenu(hmenu);
4249 if (!menu)
4250 return FALSE;
4251 if (idItem < 0 || idItem > menu->nItems)
4252 return FALSE;
4254 if (!menu->Height)
4256 SetRectEmpty(&pmbi->rcBar);
4258 else if (idItem == 0)
4260 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4261 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4262 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4264 else
4266 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4269 pmbi->hMenu = hmenu;
4270 pmbi->hwndMenu = NULL;
4271 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4272 if (idItem)
4274 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4275 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4277 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4278 if (menu)
4279 pmbi->hwndMenu = menu->hWnd;
4282 else
4284 pmbi->fFocused = pmbi->fBarFocused;
4287 return TRUE;
4290 /**********************************************************************
4291 * MENU_SetMenu
4293 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4294 * SetWindowPos call that would result if SetMenu were called directly.
4296 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4298 TRACE("(%p, %p);\n", hWnd, hMenu);
4300 if (hMenu && !IsMenu(hMenu))
4302 WARN("hMenu %p is not a menu handle\n", hMenu);
4303 return FALSE;
4305 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4306 return FALSE;
4308 hWnd = WIN_GetFullHandle( hWnd );
4309 if (GetCapture() == hWnd)
4310 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4312 if (hMenu != 0)
4314 LPPOPUPMENU lpmenu;
4316 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4318 lpmenu->hWnd = hWnd;
4319 lpmenu->Height = 0; /* Make sure we recalculate the size */
4321 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4322 return TRUE;
4326 /**********************************************************************
4327 * SetMenu (USER32.@)
4329 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4331 if(!MENU_SetMenu(hWnd, hMenu))
4332 return FALSE;
4334 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4335 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4336 return TRUE;
4340 /**********************************************************************
4341 * GetSubMenu (USER32.@)
4343 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4345 MENUITEM * lpmi;
4347 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4348 if (!(lpmi->fType & MF_POPUP)) return 0;
4349 return lpmi->hSubMenu;
4353 /**********************************************************************
4354 * DrawMenuBar (USER32.@)
4356 BOOL WINAPI DrawMenuBar( HWND hWnd )
4358 LPPOPUPMENU lppop;
4359 HMENU hMenu;
4361 if (!IsWindow( hWnd ))
4362 return FALSE;
4363 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4364 return TRUE;
4366 if ((hMenu = GetMenu( hWnd )) && (lppop = MENU_GetMenu( hMenu ))) {
4367 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4368 lppop->hwndOwner = hWnd;
4371 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4372 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4375 /***********************************************************************
4376 * DrawMenuBarTemp (USER32.@)
4378 * UNDOCUMENTED !!
4380 * called by W98SE desk.cpl Control Panel Applet
4382 * Not 100% sure about the param names, but close.
4384 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4386 LPPOPUPMENU lppop;
4387 UINT i,retvalue;
4388 HFONT hfontOld = 0;
4389 BOOL flat_menu = FALSE;
4391 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4393 if (!hMenu)
4394 hMenu = GetMenu(hwnd);
4396 if (!hFont)
4397 hFont = get_menu_font(FALSE);
4399 lppop = MENU_GetMenu( hMenu );
4400 if (lppop == NULL || lprect == NULL)
4402 retvalue = GetSystemMetrics(SM_CYMENU);
4403 goto END;
4406 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4408 hfontOld = SelectObject( hDC, hFont);
4410 if (lppop->Height == 0)
4411 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4413 lprect->bottom = lprect->top + lppop->Height;
4415 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4417 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4418 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4419 LineTo( hDC, lprect->right, lprect->bottom );
4421 if (lppop->nItems == 0)
4423 retvalue = GetSystemMetrics(SM_CYMENU);
4424 goto END;
4427 for (i = 0; i < lppop->nItems; i++)
4429 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4430 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4432 retvalue = lppop->Height;
4434 END:
4435 if (hfontOld) SelectObject (hDC, hfontOld);
4436 return retvalue;
4439 /***********************************************************************
4440 * EndMenu (USER.187)
4441 * EndMenu (USER32.@)
4443 BOOL WINAPI EndMenu(void)
4445 /* if we are in the menu code, and it is active */
4446 if (!fEndMenu && top_popup)
4448 /* terminate the menu handling code */
4449 fEndMenu = TRUE;
4451 /* needs to be posted to wakeup the internal menu handler */
4452 /* which will now terminate the menu, in the event that */
4453 /* the main window was minimized, or lost focus, so we */
4454 /* don't end up with an orphaned menu */
4455 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4457 return fEndMenu;
4461 /*****************************************************************
4462 * LoadMenuA (USER32.@)
4464 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4466 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4467 if (!hrsrc) return 0;
4468 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4472 /*****************************************************************
4473 * LoadMenuW (USER32.@)
4475 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4477 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4478 if (!hrsrc) return 0;
4479 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4483 /**********************************************************************
4484 * LoadMenuIndirectW (USER32.@)
4486 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4488 HMENU hMenu;
4489 WORD version, offset;
4490 LPCSTR p = template;
4492 version = GET_WORD(p);
4493 p += sizeof(WORD);
4494 TRACE("%p, ver %d\n", template, version );
4495 switch (version)
4497 case 0: /* standard format is version of 0 */
4498 offset = GET_WORD(p);
4499 p += sizeof(WORD) + offset;
4500 if (!(hMenu = CreateMenu())) return 0;
4501 if (!MENU_ParseResource( p, hMenu ))
4503 DestroyMenu( hMenu );
4504 return 0;
4506 return hMenu;
4507 case 1: /* extended format is version of 1 */
4508 offset = GET_WORD(p);
4509 p += sizeof(WORD) + offset;
4510 if (!(hMenu = CreateMenu())) return 0;
4511 if (!MENUEX_ParseResource( p, hMenu))
4513 DestroyMenu( hMenu );
4514 return 0;
4516 return hMenu;
4517 default:
4518 ERR("version %d not supported.\n", version);
4519 return 0;
4524 /**********************************************************************
4525 * LoadMenuIndirectA (USER32.@)
4527 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4529 return LoadMenuIndirectW( template );
4533 /**********************************************************************
4534 * IsMenu (USER32.@)
4536 BOOL WINAPI IsMenu(HMENU hmenu)
4538 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4540 if (!menu)
4542 SetLastError(ERROR_INVALID_MENU_HANDLE);
4543 return FALSE;
4545 return TRUE;
4548 /**********************************************************************
4549 * GetMenuItemInfo_common
4552 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4553 LPMENUITEMINFOW lpmii, BOOL unicode)
4555 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4557 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4559 if (!menu) {
4560 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4561 return FALSE;
4564 if( lpmii->fMask & MIIM_TYPE) {
4565 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4566 WARN("invalid combination of fMask bits used\n");
4567 /* this does not happen on Win9x/ME */
4568 SetLastError( ERROR_INVALID_PARAMETER);
4569 return FALSE;
4571 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4572 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4573 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4574 if( lpmii->fType & MFT_BITMAP) {
4575 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4576 lpmii->cch = 0;
4577 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4578 /* this does not happen on Win9x/ME */
4579 lpmii->dwTypeData = 0;
4580 lpmii->cch = 0;
4584 /* copy the text string */
4585 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4586 if( !menu->text ) {
4587 if(lpmii->dwTypeData && lpmii->cch) {
4588 if( unicode)
4589 *((WCHAR *)lpmii->dwTypeData) = 0;
4590 else
4591 *((CHAR *)lpmii->dwTypeData) = 0;
4593 lpmii->cch = 0;
4594 } else {
4595 int len;
4596 if (unicode)
4598 len = strlenW(menu->text);
4599 if(lpmii->dwTypeData && lpmii->cch)
4600 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4602 else
4604 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4605 0, NULL, NULL ) - 1;
4606 if(lpmii->dwTypeData && lpmii->cch)
4607 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4608 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4609 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4611 /* if we've copied a substring we return its length */
4612 if(lpmii->dwTypeData && lpmii->cch)
4613 if (lpmii->cch <= len + 1)
4614 lpmii->cch--;
4615 else
4616 lpmii->cch = len;
4617 else {
4618 /* return length of string */
4619 /* not on Win9x/ME if fType & MFT_BITMAP */
4620 lpmii->cch = len;
4625 if (lpmii->fMask & MIIM_FTYPE)
4626 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4628 if (lpmii->fMask & MIIM_BITMAP)
4629 lpmii->hbmpItem = menu->hbmpItem;
4631 if (lpmii->fMask & MIIM_STATE)
4632 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4634 if (lpmii->fMask & MIIM_ID)
4635 lpmii->wID = menu->wID;
4637 if (lpmii->fMask & MIIM_SUBMENU)
4638 lpmii->hSubMenu = menu->hSubMenu;
4639 else {
4640 /* hSubMenu is always cleared
4641 * (not on Win9x/ME ) */
4642 lpmii->hSubMenu = 0;
4645 if (lpmii->fMask & MIIM_CHECKMARKS) {
4646 lpmii->hbmpChecked = menu->hCheckBit;
4647 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4649 if (lpmii->fMask & MIIM_DATA)
4650 lpmii->dwItemData = menu->dwItemData;
4652 return TRUE;
4655 /**********************************************************************
4656 * GetMenuItemInfoA (USER32.@)
4658 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4659 LPMENUITEMINFOA lpmii)
4661 BOOL ret;
4662 MENUITEMINFOA mii;
4663 if( lpmii->cbSize != sizeof( mii) &&
4664 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4665 SetLastError( ERROR_INVALID_PARAMETER);
4666 return FALSE;
4668 memcpy( &mii, lpmii, lpmii->cbSize);
4669 mii.cbSize = sizeof( mii);
4670 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4671 (LPMENUITEMINFOW)&mii, FALSE);
4672 mii.cbSize = lpmii->cbSize;
4673 memcpy( lpmii, &mii, mii.cbSize);
4674 return ret;
4677 /**********************************************************************
4678 * GetMenuItemInfoW (USER32.@)
4680 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4681 LPMENUITEMINFOW lpmii)
4683 BOOL ret;
4684 MENUITEMINFOW mii;
4685 if( lpmii->cbSize != sizeof( mii) &&
4686 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4687 SetLastError( ERROR_INVALID_PARAMETER);
4688 return FALSE;
4690 memcpy( &mii, lpmii, lpmii->cbSize);
4691 mii.cbSize = sizeof( mii);
4692 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4693 mii.cbSize = lpmii->cbSize;
4694 memcpy( lpmii, &mii, mii.cbSize);
4695 return ret;
4699 /* set a menu item text from a ASCII or Unicode string */
4700 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4702 if (!text)
4703 menu->text = NULL;
4704 else if (unicode)
4706 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4707 strcpyW( menu->text, text );
4709 else
4711 LPCSTR str = (LPCSTR)text;
4712 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4713 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4714 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4719 /**********************************************************************
4720 * MENU_depth
4722 * detect if there are loops in the menu tree (or the depth is too large)
4724 static int MENU_depth( POPUPMENU *pmenu, int depth)
4726 UINT i;
4727 MENUITEM *item;
4728 int subdepth;
4730 depth++;
4731 if( depth > MAXMENUDEPTH) return depth;
4732 item = pmenu->items;
4733 subdepth = depth;
4734 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4735 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4736 if( psubmenu){
4737 int bdepth = MENU_depth( psubmenu, depth);
4738 if( bdepth > subdepth) subdepth = bdepth;
4740 if( subdepth > MAXMENUDEPTH)
4741 TRACE("<- hmenu %p\n", item->hSubMenu);
4743 return subdepth;
4747 /**********************************************************************
4748 * SetMenuItemInfo_common
4750 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4751 * MIIM_BITMAP and MIIM_STRING flags instead.
4754 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4755 const MENUITEMINFOW *lpmii,
4756 BOOL unicode)
4758 if (!menu) return FALSE;
4760 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4762 if (lpmii->fMask & MIIM_FTYPE ) {
4763 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4764 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4766 if (lpmii->fMask & MIIM_STRING ) {
4767 /* free the string when used */
4768 HeapFree(GetProcessHeap(), 0, menu->text);
4769 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4772 if (lpmii->fMask & MIIM_STATE)
4773 /* Other menu items having MFS_DEFAULT are not converted
4774 to normal items */
4775 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4777 if (lpmii->fMask & MIIM_ID)
4778 menu->wID = lpmii->wID;
4780 if (lpmii->fMask & MIIM_SUBMENU) {
4781 menu->hSubMenu = lpmii->hSubMenu;
4782 if (menu->hSubMenu) {
4783 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4784 if (subMenu) {
4785 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4786 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4787 menu->hSubMenu = 0;
4788 return FALSE;
4790 subMenu->wFlags |= MF_POPUP;
4791 menu->fType |= MF_POPUP;
4792 } else {
4793 SetLastError( ERROR_INVALID_PARAMETER);
4794 return FALSE;
4797 else
4798 menu->fType &= ~MF_POPUP;
4801 if (lpmii->fMask & MIIM_CHECKMARKS)
4803 menu->hCheckBit = lpmii->hbmpChecked;
4804 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4806 if (lpmii->fMask & MIIM_DATA)
4807 menu->dwItemData = lpmii->dwItemData;
4809 if (lpmii->fMask & MIIM_BITMAP)
4810 menu->hbmpItem = lpmii->hbmpItem;
4812 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4813 menu->fType |= MFT_SEPARATOR;
4815 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4816 return TRUE;
4819 /**********************************************************************
4820 * MENU_NormalizeMenuItemInfoStruct
4822 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4823 * check, copy and extend the MENUITEMINFO struct from the version that the application
4824 * supplied to the version used by wine source. */
4825 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4826 MENUITEMINFOW *pmii_out )
4828 /* do we recognize the size? */
4829 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4830 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4831 SetLastError( ERROR_INVALID_PARAMETER);
4832 return FALSE;
4834 /* copy the fields that we have */
4835 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4836 /* if the hbmpItem member is missing then extend */
4837 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4838 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4839 pmii_out->hbmpItem = NULL;
4841 /* test for invalid bit combinations */
4842 if( (pmii_out->fMask & MIIM_TYPE &&
4843 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4844 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4845 WARN("invalid combination of fMask bits used\n");
4846 /* this does not happen on Win9x/ME */
4847 SetLastError( ERROR_INVALID_PARAMETER);
4848 return FALSE;
4850 /* convert old style (MIIM_TYPE) to the new */
4851 if( pmii_out->fMask & MIIM_TYPE){
4852 pmii_out->fMask |= MIIM_FTYPE;
4853 if( IS_STRING_ITEM(pmii_out->fType)){
4854 pmii_out->fMask |= MIIM_STRING;
4855 } else if( (pmii_out->fType) & MFT_BITMAP){
4856 pmii_out->fMask |= MIIM_BITMAP;
4857 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4860 return TRUE;
4863 /**********************************************************************
4864 * SetMenuItemInfoA (USER32.@)
4866 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4867 const MENUITEMINFOA *lpmii)
4869 MENUITEMINFOW mii;
4871 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4873 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4875 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4876 &mii, FALSE);
4879 /**********************************************************************
4880 * SetMenuItemInfoW (USER32.@)
4882 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4883 const MENUITEMINFOW *lpmii)
4885 MENUITEMINFOW mii;
4887 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4889 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4890 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4891 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4894 /**********************************************************************
4895 * SetMenuDefaultItem (USER32.@)
4898 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4900 UINT i;
4901 POPUPMENU *menu;
4902 MENUITEM *item;
4904 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4906 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4908 /* reset all default-item flags */
4909 item = menu->items;
4910 for (i = 0; i < menu->nItems; i++, item++)
4912 item->fState &= ~MFS_DEFAULT;
4915 /* no default item */
4916 if ( -1 == uItem)
4918 return TRUE;
4921 item = menu->items;
4922 if ( bypos )
4924 if ( uItem >= menu->nItems ) return FALSE;
4925 item[uItem].fState |= MFS_DEFAULT;
4926 return TRUE;
4928 else
4930 for (i = 0; i < menu->nItems; i++, item++)
4932 if (item->wID == uItem)
4934 item->fState |= MFS_DEFAULT;
4935 return TRUE;
4940 return FALSE;
4943 /**********************************************************************
4944 * GetMenuDefaultItem (USER32.@)
4946 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4948 POPUPMENU *menu;
4949 MENUITEM * item;
4950 UINT i = 0;
4952 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4954 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4956 /* find default item */
4957 item = menu->items;
4959 /* empty menu */
4960 if (! item) return -1;
4962 while ( !( item->fState & MFS_DEFAULT ) )
4964 i++; item++;
4965 if (i >= menu->nItems ) return -1;
4968 /* default: don't return disabled items */
4969 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4971 /* search rekursiv when needed */
4972 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4974 UINT ret;
4975 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4976 if ( -1 != ret ) return ret;
4978 /* when item not found in submenu, return the popup item */
4980 return ( bypos ) ? i : item->wID;
4985 /**********************************************************************
4986 * InsertMenuItemA (USER32.@)
4988 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4989 const MENUITEMINFOA *lpmii)
4991 MENUITEM *item;
4992 MENUITEMINFOW mii;
4994 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4996 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4998 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4999 return SetMenuItemInfo_common(item, &mii, FALSE);
5003 /**********************************************************************
5004 * InsertMenuItemW (USER32.@)
5006 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5007 const MENUITEMINFOW *lpmii)
5009 MENUITEM *item;
5010 MENUITEMINFOW mii;
5012 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5014 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5016 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5017 return SetMenuItemInfo_common(item, &mii, TRUE);
5020 /**********************************************************************
5021 * CheckMenuRadioItem (USER32.@)
5024 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5025 UINT first, UINT last, UINT check,
5026 UINT bypos)
5028 BOOL done = FALSE;
5029 UINT i;
5030 MENUITEM *mi_first = NULL, *mi_check;
5031 HMENU m_first, m_check;
5033 for (i = first; i <= last; i++)
5035 UINT pos = i;
5037 if (!mi_first)
5039 m_first = hMenu;
5040 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5041 if (!mi_first) continue;
5042 mi_check = mi_first;
5043 m_check = m_first;
5045 else
5047 m_check = hMenu;
5048 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5049 if (!mi_check) continue;
5052 if (m_first != m_check) continue;
5053 if (mi_check->fType == MFT_SEPARATOR) continue;
5055 if (i == check)
5057 mi_check->fType |= MFT_RADIOCHECK;
5058 mi_check->fState |= MFS_CHECKED;
5059 done = TRUE;
5061 else
5063 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5064 mi_check->fState &= ~MFS_CHECKED;
5068 return done;
5072 /**********************************************************************
5073 * GetMenuItemRect (USER32.@)
5075 * ATTENTION: Here, the returned values in rect are the screen
5076 * coordinates of the item just like if the menu was
5077 * always on the upper left side of the application.
5080 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5081 LPRECT rect)
5083 POPUPMENU *itemMenu;
5084 MENUITEM *item;
5085 HWND referenceHwnd;
5087 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5089 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5090 referenceHwnd = hwnd;
5092 if(!hwnd)
5094 itemMenu = MENU_GetMenu(hMenu);
5095 if (itemMenu == NULL)
5096 return FALSE;
5098 if(itemMenu->hWnd == 0)
5099 return FALSE;
5100 referenceHwnd = itemMenu->hWnd;
5103 if ((rect == NULL) || (item == NULL))
5104 return FALSE;
5106 *rect = item->rect;
5108 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5110 return TRUE;
5113 /**********************************************************************
5114 * SetMenuInfo (USER32.@)
5116 * FIXME
5117 * actually use the items to draw the menu
5118 * (recalculate and/or redraw)
5120 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5122 POPUPMENU *menu;
5123 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5125 if (lpmi->fMask & MIM_BACKGROUND)
5126 menu->hbrBack = lpmi->hbrBack;
5128 if (lpmi->fMask & MIM_HELPID)
5129 menu->dwContextHelpID = lpmi->dwContextHelpID;
5131 if (lpmi->fMask & MIM_MAXHEIGHT)
5132 menu->cyMax = lpmi->cyMax;
5134 if (lpmi->fMask & MIM_MENUDATA)
5135 menu->dwMenuData = lpmi->dwMenuData;
5137 if (lpmi->fMask & MIM_STYLE)
5138 menu->dwStyle = lpmi->dwStyle;
5140 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5141 int i;
5142 MENUITEM *item = menu->items;
5143 for( i = menu->nItems; i; i--, item++)
5144 if( item->fType & MF_POPUP)
5145 menu_SetMenuInfo( item->hSubMenu, lpmi);
5147 return TRUE;
5150 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5152 TRACE("(%p %p)\n", hMenu, lpmi);
5153 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5154 if( lpmi->fMask & MIM_STYLE) {
5155 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5156 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5157 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5159 return TRUE;
5161 SetLastError( ERROR_INVALID_PARAMETER);
5162 return FALSE;
5165 /**********************************************************************
5166 * GetMenuInfo (USER32.@)
5168 * NOTES
5169 * win98/NT5.0
5172 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5173 { POPUPMENU *menu;
5175 TRACE("(%p %p)\n", hMenu, lpmi);
5177 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5180 if (lpmi->fMask & MIM_BACKGROUND)
5181 lpmi->hbrBack = menu->hbrBack;
5183 if (lpmi->fMask & MIM_HELPID)
5184 lpmi->dwContextHelpID = menu->dwContextHelpID;
5186 if (lpmi->fMask & MIM_MAXHEIGHT)
5187 lpmi->cyMax = menu->cyMax;
5189 if (lpmi->fMask & MIM_MENUDATA)
5190 lpmi->dwMenuData = menu->dwMenuData;
5192 if (lpmi->fMask & MIM_STYLE)
5193 lpmi->dwStyle = menu->dwStyle;
5195 return TRUE;
5197 SetLastError( ERROR_INVALID_PARAMETER);
5198 return FALSE;
5202 /**********************************************************************
5203 * SetMenuContextHelpId (USER32.@)
5205 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5207 LPPOPUPMENU menu;
5209 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5211 if ((menu = MENU_GetMenu(hMenu)))
5213 menu->dwContextHelpID = dwContextHelpID;
5214 return TRUE;
5216 return FALSE;
5220 /**********************************************************************
5221 * GetMenuContextHelpId (USER32.@)
5223 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5225 LPPOPUPMENU menu;
5227 TRACE("(%p)\n", hMenu);
5229 if ((menu = MENU_GetMenu(hMenu)))
5231 return menu->dwContextHelpID;
5233 return 0;
5236 /**********************************************************************
5237 * MenuItemFromPoint (USER32.@)
5239 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5241 POPUPMENU *menu = MENU_GetMenu(hMenu);
5242 UINT pos;
5244 /*FIXME: Do we have to handle hWnd here? */
5245 if (!menu) return -1;
5246 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5247 return pos;
5251 /**********************************************************************
5252 * translate_accelerator
5254 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5255 BYTE fVirt, WORD key, WORD cmd )
5257 INT mask = 0;
5258 UINT mesg = 0;
5260 if (wParam != key) return FALSE;
5262 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5263 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5264 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5266 if (message == WM_CHAR || message == WM_SYSCHAR)
5268 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5270 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5271 goto found;
5274 else
5276 if(fVirt & FVIRTKEY)
5278 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5279 wParam, 0xff & HIWORD(lParam));
5281 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5282 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5284 else
5286 if (!(lParam & 0x01000000)) /* no special_key */
5288 if ((fVirt & FALT) && (lParam & 0x20000000))
5289 { /* ^^ ALT pressed */
5290 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5291 goto found;
5296 return FALSE;
5298 found:
5299 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5300 mesg = 1;
5301 else
5303 HMENU hMenu, hSubMenu, hSysMenu;
5304 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5306 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5307 hSysMenu = get_win_sys_menu( hWnd );
5309 /* find menu item and ask application to initialize it */
5310 /* 1. in the system menu */
5311 hSubMenu = hSysMenu;
5312 nPos = cmd;
5313 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5315 if (GetCapture())
5316 mesg = 2;
5317 if (!IsWindowEnabled(hWnd))
5318 mesg = 3;
5319 else
5321 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5322 if(hSubMenu != hSysMenu)
5324 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5325 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5326 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5328 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5331 else /* 2. in the window's menu */
5333 hSubMenu = hMenu;
5334 nPos = cmd;
5335 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5337 if (GetCapture())
5338 mesg = 2;
5339 if (!IsWindowEnabled(hWnd))
5340 mesg = 3;
5341 else
5343 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5344 if(hSubMenu != hMenu)
5346 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5347 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5348 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5350 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5355 if (mesg == 0)
5357 if (uSysStat != (UINT)-1)
5359 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5360 mesg=4;
5361 else
5362 mesg=WM_SYSCOMMAND;
5364 else
5366 if (uStat != (UINT)-1)
5368 if (IsIconic(hWnd))
5369 mesg=5;
5370 else
5372 if (uStat & (MF_DISABLED|MF_GRAYED))
5373 mesg=6;
5374 else
5375 mesg=WM_COMMAND;
5378 else
5379 mesg=WM_COMMAND;
5384 if( mesg==WM_COMMAND )
5386 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5387 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5389 else if( mesg==WM_SYSCOMMAND )
5391 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5392 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5394 else
5396 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5397 * #0: unknown (please report!)
5398 * #1: for WM_KEYUP,WM_SYSKEYUP
5399 * #2: mouse is captured
5400 * #3: window is disabled
5401 * #4: it's a disabled system menu option
5402 * #5: it's a menu option, but window is iconic
5403 * #6: it's a menu option, but disabled
5405 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5406 if(mesg==0)
5407 ERR_(accel)(" unknown reason - please report!\n");
5409 return TRUE;
5412 /**********************************************************************
5413 * TranslateAcceleratorA (USER32.@)
5414 * TranslateAccelerator (USER32.@)
5416 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5418 switch (msg->message)
5420 case WM_KEYDOWN:
5421 case WM_SYSKEYDOWN:
5422 return TranslateAcceleratorW( hWnd, hAccel, msg );
5424 case WM_CHAR:
5425 case WM_SYSCHAR:
5427 MSG msgW = *msg;
5428 char ch = LOWORD(msg->wParam);
5429 WCHAR wch;
5430 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5431 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5432 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5435 default:
5436 return 0;
5440 /**********************************************************************
5441 * TranslateAcceleratorW (USER32.@)
5443 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5445 ACCEL data[32], *ptr = data;
5446 int i, count;
5448 if (!hWnd) return 0;
5450 if (msg->message != WM_KEYDOWN &&
5451 msg->message != WM_SYSKEYDOWN &&
5452 msg->message != WM_CHAR &&
5453 msg->message != WM_SYSCHAR)
5454 return 0;
5456 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5457 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5459 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5460 if (count > sizeof(data)/sizeof(data[0]))
5462 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5464 count = CopyAcceleratorTableW( hAccel, ptr, count );
5465 for (i = 0; i < count; i++)
5467 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5468 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5469 break;
5471 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5472 return (i < count);