Release 1.1.37.
[wine/gsoc-2012-control.git] / dlls / user32 / menu.c
blob6e7e6632d4b429eb594ed9f830c107f226c48a71
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class =
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 const char *postfix)
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
229 if (mp) {
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
232 if ( mp->hSubMenu)
233 TRACE( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 TRACE( ", fType=");
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 if (flags)
248 TRACE( "+0x%x", flags);
250 flags = mp->fState;
251 if (flags) {
252 int count = 0;
253 TRACE( ", State=");
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
261 if (flags)
262 TRACE( "+0x%x", flags);
264 if (mp->hCheckBit)
265 TRACE( ", Chk=%p", mp->hCheckBit);
266 if (mp->hUnCheckBit)
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 if (mp->text)
269 TRACE( ", Text=%s", debugstr_w(mp->text));
270 if (mp->dwItemData)
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if (mp->hbmpItem)
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 else
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
279 TRACE( " }");
280 } else
281 TRACE( "NULL");
282 TRACE(" %s\n", postfix);
285 #undef MENUOUT
286 #undef MENUFLAG
289 /***********************************************************************
290 * MENU_GetMenu
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
298 if (menu == OBJ_OTHER_PROCESS)
300 WARN( "other process menu %p?\n", hMenu);
301 return NULL;
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
305 return menu;
308 /***********************************************************************
309 * get_win_sys_menu
311 * Get the system menu of a window
313 static HMENU get_win_sys_menu( HWND hwnd )
315 HMENU ret = 0;
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
319 ret = win->hSysMenu;
320 WIN_ReleasePtr( win );
322 return ret;
325 /***********************************************************************
326 * get_menu_font
328 static HFONT get_menu_font( BOOL bold )
330 static HFONT hMenuFont, hMenuFontBold;
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
334 if (!ret)
336 NONCLIENTMETRICSW ncm;
337 HFONT prev;
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
342 if (bold)
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 ret, NULL );
350 if (prev)
352 /* another thread beat us to it */
353 DeleteObject( ret );
354 ret = prev;
357 return ret;
360 /***********************************************************************
361 * get_arrow_bitmap
363 static HBITMAP get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap;
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 return arrow_bitmap;
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap;
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
379 return arrow_bitmap;
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap;
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
390 return arrow_bitmap;
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap;
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
401 return arrow_bitmap;
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap;
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
412 return arrow_bitmap;
415 /***********************************************************************
416 * MENU_CopySysPopup
418 * Return the default system menu.
420 static HMENU MENU_CopySysPopup(void)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
425 if( hMenu ) {
426 MENUINFO minfo;
427 MENUITEMINFOW miteminfo;
428 POPUPMENU* menu = MENU_GetMenu(hMenu);
429 menu->wFlags |= MF_SYSMENU | MF_POPUP;
430 /* decorate the menu with bitmaps */
431 minfo.cbSize = sizeof( MENUINFO);
432 minfo.dwStyle = MNS_CHECKORBMP;
433 minfo.fMask = MIM_STYLE;
434 SetMenuInfo( hMenu, &minfo);
435 miteminfo.cbSize = sizeof( MENUITEMINFOW);
436 miteminfo.fMask = MIIM_BITMAP;
437 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
438 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
439 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
440 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
441 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
442 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
443 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
444 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
445 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
447 else
448 ERR("Unable to load default system menu\n" );
450 TRACE("returning %p.\n", hMenu );
452 return hMenu;
456 /**********************************************************************
457 * MENU_GetSysMenu
459 * Create a copy of the system menu. System menu in Windows is
460 * a special menu bar with the single entry - system menu popup.
461 * This popup is presented to the outside world as a "system menu".
462 * However, the real system menu handle is sometimes seen in the
463 * WM_MENUSELECT parameters (and Word 6 likes it this way).
465 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
467 HMENU hMenu;
469 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
470 if ((hMenu = CreateMenu()))
472 POPUPMENU *menu = MENU_GetMenu(hMenu);
473 menu->wFlags = MF_SYSMENU;
474 menu->hWnd = WIN_GetFullHandle( hWnd );
475 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
477 if (!hPopupMenu)
478 hPopupMenu = MENU_CopySysPopup();
480 if (hPopupMenu)
482 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
483 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
485 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
486 (UINT_PTR)hPopupMenu, NULL );
488 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
489 menu->items[0].fState = 0;
490 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
492 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
493 return hMenu;
495 DestroyMenu( hMenu );
497 ERR("failed to load system menu!\n");
498 return 0;
502 /***********************************************************************
503 * MENU_InitSysMenuPopup
505 * Grey the appropriate items in System menu.
507 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
509 BOOL gray;
511 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
512 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
513 gray = ((style & WS_MAXIMIZE) != 0);
514 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
515 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
516 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
517 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
518 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
520 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = (clsStyle & CS_NOCLOSE) != 0;
523 /* The menu item must keep its state if it's disabled */
524 if(gray)
525 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
529 /******************************************************************************
531 * UINT MENU_GetStartOfNextColumn(
532 * HMENU hMenu )
534 *****************************************************************************/
536 static UINT MENU_GetStartOfNextColumn(
537 HMENU hMenu )
539 POPUPMENU *menu = MENU_GetMenu(hMenu);
540 UINT i;
542 if(!menu)
543 return NO_SELECTED_ITEM;
545 i = menu->FocusedItem + 1;
546 if( i == NO_SELECTED_ITEM )
547 return i;
549 for( ; i < menu->nItems; ++i ) {
550 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
551 return i;
554 return NO_SELECTED_ITEM;
558 /******************************************************************************
560 * UINT MENU_GetStartOfPrevColumn(
561 * HMENU hMenu )
563 *****************************************************************************/
565 static UINT MENU_GetStartOfPrevColumn(
566 HMENU hMenu )
568 POPUPMENU *menu = MENU_GetMenu(hMenu);
569 UINT i;
571 if( !menu )
572 return NO_SELECTED_ITEM;
574 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
575 return NO_SELECTED_ITEM;
577 /* Find the start of the column */
579 for(i = menu->FocusedItem; i != 0 &&
580 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
581 --i); /* empty */
583 if(i == 0)
584 return NO_SELECTED_ITEM;
586 for(--i; i != 0; --i) {
587 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
588 break;
591 TRACE("ret %d.\n", i );
593 return i;
598 /***********************************************************************
599 * MENU_FindItem
601 * Find a menu item. Return a pointer on the item, and modifies *hmenu
602 * in case the item was in a sub-menu.
604 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
606 POPUPMENU *menu;
607 MENUITEM *fallback = NULL;
608 UINT fallback_pos = 0;
609 UINT i;
611 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
612 if (wFlags & MF_BYPOSITION)
614 if (*nPos >= menu->nItems) return NULL;
615 return &menu->items[*nPos];
617 else
619 MENUITEM *item = menu->items;
620 for (i = 0; i < menu->nItems; i++, item++)
622 if (item->fType & MF_POPUP)
624 HMENU hsubmenu = item->hSubMenu;
625 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
626 if (subitem)
628 *hmenu = hsubmenu;
629 return subitem;
631 else if (item->wID == *nPos)
633 /* fallback to this item if nothing else found */
634 fallback_pos = i;
635 fallback = item;
638 else if (item->wID == *nPos)
640 *nPos = i;
641 return item;
646 if (fallback)
647 *nPos = fallback_pos;
649 return fallback;
652 /***********************************************************************
653 * MENU_FindSubMenu
655 * Find a Sub menu. Return the position of the submenu, and modifies
656 * *hmenu in case it is found in another sub-menu.
657 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
659 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
661 POPUPMENU *menu;
662 UINT i;
663 MENUITEM *item;
664 if (((*hmenu)==(HMENU)0xffff) ||
665 (!(menu = MENU_GetMenu(*hmenu))))
666 return NO_SELECTED_ITEM;
667 item = menu->items;
668 for (i = 0; i < menu->nItems; i++, item++) {
669 if(!(item->fType & MF_POPUP)) continue;
670 if (item->hSubMenu == hSubTarget) {
671 return i;
673 else {
674 HMENU hsubmenu = item->hSubMenu;
675 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
676 if (pos != NO_SELECTED_ITEM) {
677 *hmenu = hsubmenu;
678 return pos;
682 return NO_SELECTED_ITEM;
685 /***********************************************************************
686 * MENU_FreeItemData
688 static void MENU_FreeItemData( MENUITEM* item )
690 /* delete text */
691 HeapFree( GetProcessHeap(), 0, item->text );
694 /***********************************************************************
695 * MENU_AdjustMenuItemRect
697 * Adjust menu item rectangle according to scrolling state.
699 static void
700 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
702 if (menu->bScrolling)
704 UINT arrow_bitmap_height;
705 BITMAP bmp;
707 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
708 arrow_bitmap_height = bmp.bmHeight;
709 rect->top += arrow_bitmap_height - menu->nScrollPos;
710 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
715 /***********************************************************************
716 * MENU_FindItemByCoords
718 * Find the item at the specified coordinates (screen coords). Does
719 * not work for child windows and therefore should not be called for
720 * an arbitrary system menu.
722 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
723 POINT pt, UINT *pos )
725 MENUITEM *item;
726 UINT i;
727 RECT rect;
729 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
730 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;
770 for (i = 0; i < menu->nItems; i++, item++)
772 if( item->text)
774 WCHAR *p = item->text - 2;
777 p = strchrW (p + 2, '&');
779 while (p != NULL && p [1] == '&');
780 if (p && (toupperW(p[1]) == toupperW(key))) return i;
784 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
785 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
786 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
787 if (HIWORD(menuchar) == 1) return (UINT)(-2);
789 return (UINT)(-1);
793 /***********************************************************************
794 * MENU_GetBitmapItemSize
796 * Get the size of a bitmap item.
798 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
799 HWND hwndOwner)
801 BITMAP bm;
802 HBITMAP bmp = lpitem->hbmpItem;
804 size->cx = size->cy = 0;
806 /* check if there is a magic menu item associated with this item */
807 switch( (INT_PTR) bmp )
809 case (INT_PTR)HBMMENU_CALLBACK:
811 MEASUREITEMSTRUCT measItem;
812 measItem.CtlType = ODT_MENU;
813 measItem.CtlID = 0;
814 measItem.itemID = lpitem->wID;
815 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
816 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
817 measItem.itemData = lpitem->dwItemData;
818 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
819 size->cx = measItem.itemWidth;
820 size->cy = measItem.itemHeight;
821 return;
823 break;
824 case (INT_PTR)HBMMENU_SYSTEM:
825 if (lpitem->dwItemData)
827 bmp = (HBITMAP)lpitem->dwItemData;
828 break;
830 /* fall through */
831 case (INT_PTR)HBMMENU_MBAR_RESTORE:
832 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
833 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
834 case (INT_PTR)HBMMENU_MBAR_CLOSE:
835 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
836 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
837 size->cy = size->cx;
838 return;
839 case (INT_PTR)HBMMENU_POPUP_CLOSE:
840 case (INT_PTR)HBMMENU_POPUP_RESTORE:
841 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
842 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
843 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
844 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
845 return;
847 if (GetObjectW(bmp, sizeof(bm), &bm ))
849 size->cx = bm.bmWidth;
850 size->cy = bm.bmHeight;
854 /***********************************************************************
855 * MENU_DrawBitmapItem
857 * Draw a bitmap item.
859 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
860 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
862 BITMAP bm;
863 DWORD rop;
864 HDC hdcMem;
865 HBITMAP bmp;
866 int w = rect->right - rect->left;
867 int h = rect->bottom - rect->top;
868 int bmp_xoffset = 0;
869 int left, top;
870 HBITMAP hbmToDraw = lpitem->hbmpItem;
871 bmp = hbmToDraw;
873 /* Check if there is a magic menu item associated with this item */
874 if (IS_MAGIC_BITMAP(hbmToDraw))
876 UINT flags = 0;
877 WCHAR bmchr = 0;
878 RECT r;
880 switch((INT_PTR)hbmToDraw)
882 case (INT_PTR)HBMMENU_SYSTEM:
883 if (lpitem->dwItemData)
885 bmp = (HBITMAP)lpitem->dwItemData;
886 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
888 else
890 static HBITMAP hBmpSysMenu;
892 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
893 bmp = hBmpSysMenu;
894 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
895 /* only use right half of the bitmap */
896 bmp_xoffset = bm.bmWidth / 2;
897 bm.bmWidth -= bmp_xoffset;
899 goto got_bitmap;
900 case (INT_PTR)HBMMENU_MBAR_RESTORE:
901 flags = DFCS_CAPTIONRESTORE;
902 break;
903 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
904 flags = DFCS_CAPTIONMIN;
905 break;
906 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
907 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
908 break;
909 case (INT_PTR)HBMMENU_MBAR_CLOSE:
910 flags = DFCS_CAPTIONCLOSE;
911 break;
912 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
913 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
914 break;
915 case (INT_PTR)HBMMENU_CALLBACK:
917 DRAWITEMSTRUCT drawItem;
918 drawItem.CtlType = ODT_MENU;
919 drawItem.CtlID = 0;
920 drawItem.itemID = lpitem->wID;
921 drawItem.itemAction = odaction;
922 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
923 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
924 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
925 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
926 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
927 drawItem.hwndItem = (HWND)hmenu;
928 drawItem.hDC = hdc;
929 drawItem.itemData = lpitem->dwItemData;
930 drawItem.rcItem = *rect;
931 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
932 return;
934 break;
935 case (INT_PTR)HBMMENU_POPUP_CLOSE:
936 bmchr = 0x72;
937 break;
938 case (INT_PTR)HBMMENU_POPUP_RESTORE:
939 bmchr = 0x32;
940 break;
941 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
942 bmchr = 0x31;
943 break;
944 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
945 bmchr = 0x30;
946 break;
947 default:
948 FIXME("Magic %p not implemented\n", hbmToDraw);
949 return;
951 if (bmchr)
953 /* draw the magic bitmaps using marlett font characters */
954 /* FIXME: fontsize and the position (x,y) could probably be better */
955 HFONT hfont, hfontsav;
956 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
957 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
958 { 'M','a','r','l','e','t','t',0 } };
959 logfont.lfHeight = min( h, w) - 5 ;
960 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
961 hfont = CreateFontIndirectW( &logfont);
962 hfontsav = SelectObject(hdc, hfont);
963 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
964 SelectObject(hdc, hfontsav);
965 DeleteObject( hfont);
967 else
969 r = *rect;
970 InflateRect( &r, -1, -1 );
971 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
972 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
974 return;
977 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
979 got_bitmap:
980 hdcMem = CreateCompatibleDC( hdc );
981 SelectObject( hdcMem, bmp );
983 /* handle fontsize > bitmap_height */
984 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
985 left=rect->left;
986 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
987 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
988 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
989 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
990 DeleteDC( hdcMem );
994 /***********************************************************************
995 * MENU_CalcItemSize
997 * Calculate the size of the menu item and store it in lpitem->rect.
999 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1000 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1002 WCHAR *p;
1003 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1004 UINT arrow_bitmap_width;
1005 BITMAP bm;
1006 INT itemheight;
1008 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1009 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1010 (menuBar ? " (MenuBar)" : ""));
1012 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1013 arrow_bitmap_width = bm.bmWidth;
1015 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1016 if( !menucharsize.cx ) {
1017 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1018 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1019 * but it is unlikely an application will depend on that */
1020 ODitemheight = HIWORD( GetDialogBaseUnits());
1023 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1025 if (lpitem->fType & MF_OWNERDRAW)
1027 MEASUREITEMSTRUCT mis;
1028 mis.CtlType = ODT_MENU;
1029 mis.CtlID = 0;
1030 mis.itemID = lpitem->wID;
1031 mis.itemData = lpitem->dwItemData;
1032 mis.itemHeight = ODitemheight;
1033 mis.itemWidth = 0;
1034 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1035 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1036 * width of a menufont character to the width of an owner-drawn menu.
1038 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1039 if (menuBar) {
1040 /* under at least win95 you seem to be given a standard
1041 height for the menu and the height value is ignored */
1042 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1043 } else
1044 lpitem->rect.bottom += mis.itemHeight;
1046 TRACE("id=%04lx size=%dx%d\n",
1047 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1048 lpitem->rect.bottom-lpitem->rect.top);
1049 return;
1052 if (lpitem->fType & MF_SEPARATOR)
1054 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1055 if( !menuBar)
1056 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1057 return;
1060 itemheight = 0;
1061 lpitem->xTab = 0;
1063 if (!menuBar) {
1064 if (lpitem->hbmpItem) {
1065 SIZE size;
1067 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1068 /* Keep the size of the bitmap in callback mode to be able
1069 * to draw it correctly */
1070 lpitem->bmpsize = size;
1071 lppop->textOffset = max( lppop->textOffset, size.cx);
1072 lpitem->rect.right += size.cx + 2;
1073 itemheight = size.cy + 2;
1075 if( !(lppop->dwStyle & MNS_NOCHECK))
1076 lpitem->rect.right += check_bitmap_width;
1077 lpitem->rect.right += 4 + menucharsize.cx;
1078 lpitem->xTab = lpitem->rect.right;
1079 lpitem->rect.right += arrow_bitmap_width;
1080 } else if (lpitem->hbmpItem) { /* menuBar */
1081 SIZE size;
1083 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1084 lpitem->bmpsize = size;
1085 lpitem->rect.right += size.cx;
1086 if( lpitem->text) lpitem->rect.right += 2;
1087 itemheight = size.cy;
1090 /* it must be a text item - unless it's the system menu */
1091 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1092 HFONT hfontOld = NULL;
1093 RECT rc = lpitem->rect;
1094 LONG txtheight, txtwidth;
1096 if ( lpitem->fState & MFS_DEFAULT ) {
1097 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1099 if (menuBar) {
1100 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1101 DT_SINGLELINE|DT_CALCRECT);
1102 lpitem->rect.right += rc.right - rc.left;
1103 itemheight = max( max( itemheight, txtheight),
1104 GetSystemMetrics( SM_CYMENU) - 1);
1105 lpitem->rect.right += 2 * menucharsize.cx;
1106 } else {
1107 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1108 RECT tmprc = rc;
1109 LONG tmpheight;
1110 int n = (int)( p - lpitem->text);
1111 /* Item contains a tab (only meaningful in popup menus) */
1112 /* get text size before the tab */
1113 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1114 DT_SINGLELINE|DT_CALCRECT);
1115 txtwidth = rc.right - rc.left;
1116 p += 1; /* advance past the Tab */
1117 /* get text size after the tab */
1118 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1119 DT_SINGLELINE|DT_CALCRECT);
1120 lpitem->xTab += txtwidth;
1121 txtheight = max( txtheight, tmpheight);
1122 txtwidth += menucharsize.cx + /* space for the tab */
1123 tmprc.right - tmprc.left; /* space for the short cut */
1124 } else {
1125 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1126 DT_SINGLELINE|DT_CALCRECT);
1127 txtwidth = rc.right - rc.left;
1128 lpitem->xTab += txtwidth;
1130 lpitem->rect.right += 2 + txtwidth;
1131 itemheight = max( itemheight,
1132 max( txtheight + 2, menucharsize.cy + 4));
1134 if (hfontOld) SelectObject (hdc, hfontOld);
1135 } else if( menuBar) {
1136 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1138 lpitem->rect.bottom += itemheight;
1139 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1143 /***********************************************************************
1144 * MENU_GetMaxPopupHeight
1146 static UINT
1147 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1149 if (lppop->cyMax)
1150 return lppop->cyMax;
1151 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1155 /***********************************************************************
1156 * MENU_PopupMenuCalcSize
1158 * Calculate the size of a popup menu.
1160 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1162 MENUITEM *lpitem;
1163 HDC hdc;
1164 UINT start, i;
1165 int textandbmp = FALSE;
1166 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1168 lppop->Width = lppop->Height = 0;
1169 if (lppop->nItems == 0) return;
1170 hdc = GetDC( 0 );
1172 SelectObject( hdc, get_menu_font(FALSE));
1174 start = 0;
1175 maxX = 2 + 1;
1177 lppop->textOffset = 0;
1179 while (start < lppop->nItems)
1181 lpitem = &lppop->items[start];
1182 orgX = maxX;
1183 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1184 orgX += MENU_COL_SPACE;
1185 orgY = MENU_TOP_MARGIN;
1187 maxTab = maxTabWidth = 0;
1188 /* Parse items until column break or end of menu */
1189 for (i = start; i < lppop->nItems; i++, lpitem++)
1191 if ((i != start) &&
1192 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1194 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1195 maxX = max( maxX, lpitem->rect.right );
1196 orgY = lpitem->rect.bottom;
1197 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1199 maxTab = max( maxTab, lpitem->xTab );
1200 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1202 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1205 /* Finish the column (set all items to the largest width found) */
1206 maxX = max( maxX, maxTab + maxTabWidth );
1207 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1209 lpitem->rect.right = maxX;
1210 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1211 lpitem->xTab = maxTab;
1214 lppop->Height = max( lppop->Height, orgY );
1217 lppop->Width = maxX;
1218 /* if none of the items have both text and bitmap then
1219 * the text and bitmaps are all aligned on the left. If there is at
1220 * least one item with both text and bitmap then bitmaps are
1221 * on the left and texts left aligned with the right hand side
1222 * of the bitmaps */
1223 if( !textandbmp) lppop->textOffset = 0;
1225 /* space for 3d border */
1226 lppop->Height += MENU_BOTTOM_MARGIN;
1227 lppop->Width += 2;
1229 /* Adjust popup height if it exceeds maximum */
1230 maxHeight = MENU_GetMaxPopupHeight(lppop);
1231 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1232 if (lppop->Height >= maxHeight)
1234 lppop->Height = maxHeight;
1235 lppop->bScrolling = TRUE;
1237 else
1239 lppop->bScrolling = FALSE;
1242 ReleaseDC( 0, hdc );
1246 /***********************************************************************
1247 * MENU_MenuBarCalcSize
1249 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1250 * height is off by 1 pixel which causes lengthy window relocations when
1251 * active document window is maximized/restored.
1253 * Calculate the size of the menu bar.
1255 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1256 LPPOPUPMENU lppop, HWND hwndOwner )
1258 MENUITEM *lpitem;
1259 UINT start, i, helpPos;
1260 int orgX, orgY, maxY;
1262 if ((lprect == NULL) || (lppop == NULL)) return;
1263 if (lppop->nItems == 0) return;
1264 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1265 lppop->Width = lprect->right - lprect->left;
1266 lppop->Height = 0;
1267 maxY = lprect->top+1;
1268 start = 0;
1269 helpPos = ~0U;
1270 lppop->textOffset = 0;
1271 while (start < lppop->nItems)
1273 lpitem = &lppop->items[start];
1274 orgX = lprect->left;
1275 orgY = maxY;
1277 /* Parse items until line break or end of menu */
1278 for (i = start; i < lppop->nItems; i++, lpitem++)
1280 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1281 if ((i != start) &&
1282 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1284 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1285 debug_print_menuitem (" item: ", lpitem, "");
1286 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1288 if (lpitem->rect.right > lprect->right)
1290 if (i != start) break;
1291 else lpitem->rect.right = lprect->right;
1293 maxY = max( maxY, lpitem->rect.bottom );
1294 orgX = lpitem->rect.right;
1297 /* Finish the line (set all items to the largest height found) */
1298 while (start < i) lppop->items[start++].rect.bottom = maxY;
1301 lprect->bottom = maxY;
1302 lppop->Height = lprect->bottom - lprect->top;
1304 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1305 /* the last item (if several lines, only move the last line) */
1306 if (helpPos == ~0U) return;
1307 lpitem = &lppop->items[lppop->nItems-1];
1308 orgY = lpitem->rect.top;
1309 orgX = lprect->right;
1310 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1311 if (lpitem->rect.top != orgY) break; /* Other line */
1312 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1313 lpitem->rect.left += orgX - lpitem->rect.right;
1314 lpitem->rect.right = orgX;
1315 orgX = lpitem->rect.left;
1320 /***********************************************************************
1321 * MENU_DrawScrollArrows
1323 * Draw scroll arrows.
1325 static void
1326 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1328 HDC hdcMem = CreateCompatibleDC(hdc);
1329 HBITMAP hOrigBitmap;
1330 UINT arrow_bitmap_width, arrow_bitmap_height;
1331 BITMAP bmp;
1332 RECT rect;
1334 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1335 arrow_bitmap_width = bmp.bmWidth;
1336 arrow_bitmap_height = bmp.bmHeight;
1339 if (lppop->nScrollPos)
1340 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1341 else
1342 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1343 rect.left = 0;
1344 rect.top = 0;
1345 rect.right = lppop->Width;
1346 rect.bottom = arrow_bitmap_height;
1347 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1348 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1349 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1350 rect.top = lppop->Height - arrow_bitmap_height;
1351 rect.bottom = lppop->Height;
1352 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1353 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1354 SelectObject(hdcMem, get_down_arrow_bitmap());
1355 else
1356 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1357 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1358 lppop->Height - arrow_bitmap_height,
1359 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1360 SelectObject(hdcMem, hOrigBitmap);
1361 DeleteDC(hdcMem);
1365 /***********************************************************************
1366 * draw_popup_arrow
1368 * Draws the popup-menu arrow.
1370 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1371 UINT arrow_bitmap_height)
1373 HDC hdcMem = CreateCompatibleDC( hdc );
1374 HBITMAP hOrigBitmap;
1376 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1377 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1378 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1379 arrow_bitmap_width, arrow_bitmap_height,
1380 hdcMem, 0, 0, SRCCOPY );
1381 SelectObject( hdcMem, hOrigBitmap );
1382 DeleteDC( hdcMem );
1384 /***********************************************************************
1385 * MENU_DrawMenuItem
1387 * Draw a single menu item.
1389 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1390 UINT height, BOOL menuBar, UINT odaction )
1392 RECT rect;
1393 BOOL flat_menu = FALSE;
1394 int bkgnd;
1395 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1396 POPUPMENU *menu = MENU_GetMenu(hmenu);
1397 RECT bmprc;
1399 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1401 if (!menuBar) {
1402 BITMAP bmp;
1403 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1404 arrow_bitmap_width = bmp.bmWidth;
1405 arrow_bitmap_height = bmp.bmHeight;
1408 if (lpitem->fType & MF_SYSMENU)
1410 if( !IsIconic(hwnd) )
1411 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1412 return;
1415 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1416 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1418 /* Setup colors */
1420 if (lpitem->fState & MF_HILITE)
1422 if(menuBar && !flat_menu) {
1423 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1424 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1425 } else {
1426 if(lpitem->fState & MF_GRAYED)
1427 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1428 else
1429 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1430 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1433 else
1435 if (lpitem->fState & MF_GRAYED)
1436 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1437 else
1438 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1439 SetBkColor( hdc, GetSysColor( bkgnd ) );
1442 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1443 rect = lpitem->rect;
1444 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1446 if (lpitem->fType & MF_OWNERDRAW)
1449 ** Experimentation under Windows reveals that an owner-drawn
1450 ** menu is given the rectangle which includes the space it requested
1451 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1452 ** and a popup-menu arrow. This is the value of lpitem->rect.
1453 ** Windows will leave all drawing to the application except for
1454 ** the popup-menu arrow. Windows always draws that itself, after
1455 ** the menu owner has finished drawing.
1457 DRAWITEMSTRUCT dis;
1459 dis.CtlType = ODT_MENU;
1460 dis.CtlID = 0;
1461 dis.itemID = lpitem->wID;
1462 dis.itemData = lpitem->dwItemData;
1463 dis.itemState = 0;
1464 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1465 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1466 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1467 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1468 dis.hwndItem = (HWND)hmenu;
1469 dis.hDC = hdc;
1470 dis.rcItem = rect;
1471 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1472 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1473 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1474 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1475 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1476 /* Draw the popup-menu arrow */
1477 if (lpitem->fType & MF_POPUP)
1478 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1479 arrow_bitmap_height);
1480 return;
1483 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1485 if (lpitem->fState & MF_HILITE)
1487 if (flat_menu)
1489 InflateRect (&rect, -1, -1);
1490 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1491 InflateRect (&rect, 1, 1);
1492 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1494 else
1496 if(menuBar)
1497 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1498 else
1499 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1502 else
1503 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1505 SetBkMode( hdc, TRANSPARENT );
1507 /* vertical separator */
1508 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1510 HPEN oldPen;
1511 RECT rc = rect;
1513 rc.left -= MENU_COL_SPACE / 2 + 1;
1514 rc.top = 3;
1515 rc.bottom = height - 3;
1516 if (flat_menu)
1518 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1519 MoveToEx( hdc, rc.left, rc.top, NULL );
1520 LineTo( hdc, rc.left, rc.bottom );
1521 SelectObject( hdc, oldPen );
1523 else
1524 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1527 /* horizontal separator */
1528 if (lpitem->fType & MF_SEPARATOR)
1530 HPEN oldPen;
1531 RECT rc = rect;
1533 rc.left++;
1534 rc.right--;
1535 rc.top = ( rc.top + rc.bottom) / 2;
1536 if (flat_menu)
1538 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1539 MoveToEx( hdc, rc.left, rc.top, NULL );
1540 LineTo( hdc, rc.right, rc.top );
1541 SelectObject( hdc, oldPen );
1543 else
1544 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1545 return;
1548 /* helper lines for debugging */
1549 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1550 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1551 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1552 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1555 if (lpitem->hbmpItem) {
1556 /* calculate the bitmap rectangle in coordinates relative
1557 * to the item rectangle */
1558 if( menuBar) {
1559 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1560 bmprc.left = 3;
1561 else
1562 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1564 else if (menu->dwStyle & MNS_NOCHECK)
1565 bmprc.left = 4;
1566 else if (menu->dwStyle & MNS_CHECKORBMP)
1567 bmprc.left = 2;
1568 else
1569 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1570 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1571 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1572 bmprc.top = 0;
1573 else
1574 bmprc.top = (rect.bottom - rect.top -
1575 lpitem->bmpsize.cy) / 2;
1576 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1579 if (!menuBar)
1581 HBITMAP bm;
1582 INT y = rect.top + rect.bottom;
1583 RECT rc = rect;
1584 int checked = FALSE;
1585 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1586 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1587 /* Draw the check mark
1589 * FIXME:
1590 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1592 if( !(menu->dwStyle & MNS_NOCHECK)) {
1593 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1594 lpitem->hUnCheckBit;
1595 if (bm) /* we have a custom bitmap */
1597 HDC hdcMem = CreateCompatibleDC( hdc );
1599 SelectObject( hdcMem, bm );
1600 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1601 check_bitmap_width, check_bitmap_height,
1602 hdcMem, 0, 0, SRCCOPY );
1603 DeleteDC( hdcMem );
1604 checked = TRUE;
1606 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1608 RECT r;
1609 HBITMAP bm = CreateBitmap( check_bitmap_width,
1610 check_bitmap_height, 1, 1, NULL );
1611 HDC hdcMem = CreateCompatibleDC( hdc );
1613 SelectObject( hdcMem, bm );
1614 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1615 DrawFrameControl( hdcMem, &r, DFC_MENU,
1616 (lpitem->fType & MFT_RADIOCHECK) ?
1617 DFCS_MENUBULLET : DFCS_MENUCHECK );
1618 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1619 hdcMem, 0, 0, SRCCOPY );
1620 DeleteDC( hdcMem );
1621 DeleteObject( bm );
1622 checked = TRUE;
1625 if( lpitem->hbmpItem &&
1626 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1627 POINT origorg;
1628 /* some applications make this assumption on the DC's origin */
1629 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1630 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1631 odaction, FALSE);
1632 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1634 /* Draw the popup-menu arrow */
1635 if (lpitem->fType & MF_POPUP)
1636 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1637 arrow_bitmap_height);
1638 rect.left += 4;
1639 if( !(menu->dwStyle & MNS_NOCHECK))
1640 rect.left += check_bitmap_width;
1641 rect.right -= arrow_bitmap_width;
1643 else if( lpitem->hbmpItem)
1644 { /* Draw the bitmap */
1645 POINT origorg;
1647 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1648 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1649 odaction, menuBar);
1650 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1652 /* process text if present */
1653 if (lpitem->text)
1655 register int i;
1656 HFONT hfontOld = 0;
1658 UINT uFormat = (menuBar) ?
1659 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1660 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1662 if( !(menu->dwStyle & MNS_CHECKORBMP))
1663 rect.left += menu->textOffset;
1665 if ( lpitem->fState & MFS_DEFAULT )
1667 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1670 if (menuBar) {
1671 if( lpitem->hbmpItem)
1672 rect.left += lpitem->bmpsize.cx;
1673 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1674 rect.left += menucharsize.cx;
1675 rect.right -= menucharsize.cx;
1678 for (i = 0; lpitem->text[i]; i++)
1679 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1680 break;
1682 if(lpitem->fState & MF_GRAYED)
1684 if (!(lpitem->fState & MF_HILITE) )
1686 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1687 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1688 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1689 --rect.left; --rect.top; --rect.right; --rect.bottom;
1691 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1694 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1696 /* paint the shortcut text */
1697 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1699 if (lpitem->text[i] == '\t')
1701 rect.left = lpitem->xTab;
1702 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1704 else
1706 rect.right = lpitem->xTab;
1707 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1710 if(lpitem->fState & MF_GRAYED)
1712 if (!(lpitem->fState & MF_HILITE) )
1714 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1715 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1716 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1717 --rect.left; --rect.top; --rect.right; --rect.bottom;
1719 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1721 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1724 if (hfontOld)
1725 SelectObject (hdc, hfontOld);
1730 /***********************************************************************
1731 * MENU_DrawPopupMenu
1733 * Paint a popup menu.
1735 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1737 HBRUSH hPrevBrush = 0;
1738 RECT rect;
1740 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1742 GetClientRect( hwnd, &rect );
1744 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1745 && (SelectObject( hdc, get_menu_font(FALSE))))
1747 HPEN hPrevPen;
1749 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1751 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1752 if( hPrevPen )
1754 POPUPMENU *menu;
1755 BOOL flat_menu = FALSE;
1757 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1758 if (flat_menu)
1759 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1760 else
1761 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1763 if( (menu = MENU_GetMenu( hmenu )))
1765 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1766 /* draw menu items */
1767 if( menu->nItems)
1769 MENUITEM *item;
1770 UINT u;
1772 item = menu->items;
1773 for( u = menu->nItems; u > 0; u--, item++)
1774 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1775 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1777 /* draw scroll arrows */
1778 if (menu->bScrolling)
1779 MENU_DrawScrollArrows(menu, hdc);
1781 } else
1783 SelectObject( hdc, hPrevBrush );
1788 /***********************************************************************
1789 * MENU_DrawMenuBar
1791 * Paint a menu bar. Returns the height of the menu bar.
1792 * called from [windows/nonclient.c]
1794 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1795 BOOL suppress_draw)
1797 LPPOPUPMENU lppop;
1798 HFONT hfontOld = 0;
1799 HMENU hMenu = GetMenu(hwnd);
1801 lppop = MENU_GetMenu( hMenu );
1802 if (lppop == NULL || lprect == NULL)
1804 return GetSystemMetrics(SM_CYMENU);
1807 if (suppress_draw)
1809 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1811 if (lppop->Height == 0)
1812 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1814 lprect->bottom = lprect->top + lppop->Height;
1816 if (hfontOld) SelectObject( hDC, hfontOld);
1817 return lppop->Height;
1819 else
1820 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1824 /***********************************************************************
1825 * MENU_ShowPopup
1827 * Display a popup menu.
1829 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1830 INT x, INT y, INT xanchor, INT yanchor )
1832 POPUPMENU *menu;
1833 INT width, height;
1834 POINT pt;
1835 HMONITOR monitor;
1836 MONITORINFO info;
1838 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1839 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1841 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1842 if (menu->FocusedItem != NO_SELECTED_ITEM)
1844 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1845 menu->FocusedItem = NO_SELECTED_ITEM;
1848 /* store the owner for DrawItem */
1849 menu->hwndOwner = hwndOwner;
1851 menu->nScrollPos = 0;
1852 MENU_PopupMenuCalcSize( menu );
1854 /* adjust popup menu pos so that it fits within the desktop */
1856 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1857 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1859 /* FIXME: should use item rect */
1860 pt.x = x;
1861 pt.y = y;
1862 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1863 info.cbSize = sizeof(info);
1864 GetMonitorInfoW( monitor, &info );
1866 if( flags & TPM_RIGHTALIGN ) x -= width;
1867 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1869 if( flags & TPM_BOTTOMALIGN ) y -= height;
1870 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1872 if( x + width > info.rcWork.right)
1874 if( xanchor && x >= width - xanchor )
1875 x -= width - xanchor;
1877 if( x + width > info.rcWork.right)
1878 x = info.rcWork.right - width;
1880 if( x < info.rcWork.left ) x = info.rcWork.left;
1882 if( y + height > info.rcWork.bottom)
1884 if( yanchor && y >= height + yanchor )
1885 y -= height + yanchor;
1887 if( y + height > info.rcWork.bottom)
1888 y = info.rcWork.bottom - height;
1890 if( y < info.rcWork.top ) y = info.rcWork.top;
1892 /* NOTE: In Windows, top menu popup is not owned. */
1893 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1894 WS_POPUP, x, y, width, height,
1895 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1896 (LPVOID)hmenu );
1897 if( !menu->hWnd ) return FALSE;
1898 if (!top_popup) {
1899 top_popup = menu->hWnd;
1900 top_popup_hmenu = hmenu;
1902 /* Display the window */
1904 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1905 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1906 UpdateWindow( menu->hWnd );
1907 return TRUE;
1911 /***********************************************************************
1912 * MENU_EnsureMenuItemVisible
1914 static void
1915 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1917 if (lppop->bScrolling)
1919 MENUITEM *item = &lppop->items[wIndex];
1920 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1921 UINT nOldPos = lppop->nScrollPos;
1922 RECT rc;
1923 UINT arrow_bitmap_height;
1924 BITMAP bmp;
1926 GetClientRect(lppop->hWnd, &rc);
1928 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1929 arrow_bitmap_height = bmp.bmHeight;
1931 rc.top += arrow_bitmap_height;
1932 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1934 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1935 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1938 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1939 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1940 MENU_DrawScrollArrows(lppop, hdc);
1942 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1944 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1945 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1946 MENU_DrawScrollArrows(lppop, hdc);
1952 /***********************************************************************
1953 * MENU_SelectItem
1955 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1956 BOOL sendMenuSelect, HMENU topmenu )
1958 LPPOPUPMENU lppop;
1959 HDC hdc;
1961 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1963 lppop = MENU_GetMenu( hmenu );
1964 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1966 if (lppop->FocusedItem == wIndex) return;
1967 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1968 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1969 if (!top_popup) {
1970 top_popup = lppop->hWnd;
1971 top_popup_hmenu = hmenu;
1974 SelectObject( hdc, get_menu_font(FALSE));
1976 /* Clear previous highlighted item */
1977 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1979 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1980 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1981 lppop->Height, !(lppop->wFlags & MF_POPUP),
1982 ODA_SELECT );
1985 /* Highlight new item (if any) */
1986 lppop->FocusedItem = wIndex;
1987 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1989 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1990 lppop->items[wIndex].fState |= MF_HILITE;
1991 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1992 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1993 &lppop->items[wIndex], lppop->Height,
1994 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1996 if (sendMenuSelect)
1998 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1999 SendMessageW( hwndOwner, WM_MENUSELECT,
2000 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2001 ip->fType | ip->fState |
2002 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2005 else if (sendMenuSelect) {
2006 if(topmenu){
2007 int pos;
2008 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2009 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2010 MENUITEM *ip = &ptm->items[pos];
2011 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2012 ip->fType | ip->fState |
2013 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2017 ReleaseDC( lppop->hWnd, hdc );
2021 /***********************************************************************
2022 * MENU_MoveSelection
2024 * Moves currently selected item according to the offset parameter.
2025 * If there is no selection then it should select the last item if
2026 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2028 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2030 INT i;
2031 POPUPMENU *menu;
2033 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2035 menu = MENU_GetMenu( hmenu );
2036 if ((!menu) || (!menu->items)) return;
2038 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2040 if( menu->nItems == 1 ) return; else
2041 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2042 ; i += offset)
2043 if (!(menu->items[i].fType & MF_SEPARATOR))
2045 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2046 return;
2050 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2051 i >= 0 && i < menu->nItems ; i += offset)
2052 if (!(menu->items[i].fType & MF_SEPARATOR))
2054 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2055 return;
2060 /**********************************************************************
2061 * MENU_InsertItem
2063 * Insert (allocate) a new item into a menu.
2065 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2067 MENUITEM *newItems;
2068 POPUPMENU *menu;
2070 if (!(menu = MENU_GetMenu(hMenu)))
2071 return NULL;
2073 /* Find where to insert new item */
2075 if (flags & MF_BYPOSITION) {
2076 if (pos > menu->nItems)
2077 pos = menu->nItems;
2078 } else {
2079 if (!MENU_FindItem( &hMenu, &pos, flags ))
2080 pos = menu->nItems;
2081 else {
2082 if (!(menu = MENU_GetMenu( hMenu )))
2083 return NULL;
2087 /* Make sure that MDI system buttons stay on the right side.
2088 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2089 * regardless of their id.
2091 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2092 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2093 pos--;
2095 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2097 /* Create new items array */
2099 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2100 if (!newItems)
2102 WARN("allocation failed\n" );
2103 return NULL;
2105 if (menu->nItems > 0)
2107 /* Copy the old array into the new one */
2108 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2109 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2110 (menu->nItems-pos)*sizeof(MENUITEM) );
2111 HeapFree( GetProcessHeap(), 0, menu->items );
2113 menu->items = newItems;
2114 menu->nItems++;
2115 memset( &newItems[pos], 0, sizeof(*newItems) );
2116 menu->Height = 0; /* force size recalculate */
2117 return &newItems[pos];
2121 /**********************************************************************
2122 * MENU_ParseResource
2124 * Parse a standard menu resource and add items to the menu.
2125 * Return a pointer to the end of the resource.
2127 * NOTE: flags is equivalent to the mtOption field
2129 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2131 WORD flags, id = 0;
2132 LPCWSTR str;
2133 BOOL end_flag;
2137 flags = GET_WORD(res);
2138 end_flag = flags & MF_END;
2139 /* Remove MF_END because it has the same value as MF_HILITE */
2140 flags &= ~MF_END;
2141 res += sizeof(WORD);
2142 if (!(flags & MF_POPUP))
2144 id = GET_WORD(res);
2145 res += sizeof(WORD);
2147 str = (LPCWSTR)res;
2148 res += (strlenW(str) + 1) * sizeof(WCHAR);
2149 if (flags & MF_POPUP)
2151 HMENU hSubMenu = CreatePopupMenu();
2152 if (!hSubMenu) return NULL;
2153 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2154 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2156 else /* Not a popup */
2158 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2160 } while (!end_flag);
2161 return res;
2165 /**********************************************************************
2166 * MENUEX_ParseResource
2168 * Parse an extended menu resource and add items to the menu.
2169 * Return a pointer to the end of the resource.
2171 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2173 WORD resinfo;
2174 do {
2175 MENUITEMINFOW mii;
2177 mii.cbSize = sizeof(mii);
2178 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2179 mii.fType = GET_DWORD(res);
2180 res += sizeof(DWORD);
2181 mii.fState = GET_DWORD(res);
2182 res += sizeof(DWORD);
2183 mii.wID = GET_DWORD(res);
2184 res += sizeof(DWORD);
2185 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2186 res += sizeof(WORD);
2187 /* Align the text on a word boundary. */
2188 res += (~((UINT_PTR)res - 1)) & 1;
2189 mii.dwTypeData = (LPWSTR) res;
2190 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2191 /* Align the following fields on a dword boundary. */
2192 res += (~((UINT_PTR)res - 1)) & 3;
2194 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2195 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2197 if (resinfo & 1) { /* Pop-up? */
2198 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2199 res += sizeof(DWORD);
2200 mii.hSubMenu = CreatePopupMenu();
2201 if (!mii.hSubMenu)
2202 return NULL;
2203 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2204 DestroyMenu(mii.hSubMenu);
2205 return NULL;
2207 mii.fMask |= MIIM_SUBMENU;
2208 mii.fType |= MF_POPUP;
2210 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2212 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2213 mii.wID, mii.fType);
2214 mii.fType |= MF_SEPARATOR;
2216 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2217 } while (!(resinfo & MF_END));
2218 return res;
2222 /***********************************************************************
2223 * MENU_GetSubPopup
2225 * Return the handle of the selected sub-popup menu (if any).
2227 static HMENU MENU_GetSubPopup( HMENU hmenu )
2229 POPUPMENU *menu;
2230 MENUITEM *item;
2232 menu = MENU_GetMenu( hmenu );
2234 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2236 item = &menu->items[menu->FocusedItem];
2237 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2238 return item->hSubMenu;
2239 return 0;
2243 /***********************************************************************
2244 * MENU_HideSubPopups
2246 * Hide the sub-popup menus of this menu.
2248 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2249 BOOL sendMenuSelect, UINT wFlags )
2251 POPUPMENU *menu = MENU_GetMenu( hmenu );
2253 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2255 if (menu && top_popup)
2257 HMENU hsubmenu;
2258 POPUPMENU *submenu;
2259 MENUITEM *item;
2261 if (menu->FocusedItem != NO_SELECTED_ITEM)
2263 item = &menu->items[menu->FocusedItem];
2264 if (!(item->fType & MF_POPUP) ||
2265 !(item->fState & MF_MOUSESELECT)) return;
2266 item->fState &= ~MF_MOUSESELECT;
2267 hsubmenu = item->hSubMenu;
2268 } else return;
2270 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2271 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2272 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2273 DestroyWindow( submenu->hWnd );
2274 submenu->hWnd = 0;
2276 if (!(wFlags & TPM_NONOTIFY))
2277 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2278 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2283 /***********************************************************************
2284 * MENU_ShowSubPopup
2286 * Display the sub-menu of the selected item of this menu.
2287 * Return the handle of the submenu, or hmenu if no submenu to display.
2289 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2290 BOOL selectFirst, UINT wFlags )
2292 RECT rect;
2293 POPUPMENU *menu;
2294 MENUITEM *item;
2295 HDC hdc;
2297 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2299 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2301 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2303 item = &menu->items[menu->FocusedItem];
2304 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2305 return hmenu;
2307 /* message must be sent before using item,
2308 because nearly everything may be changed by the application ! */
2310 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2311 if (!(wFlags & TPM_NONOTIFY))
2312 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2313 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2315 item = &menu->items[menu->FocusedItem];
2316 rect = item->rect;
2318 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2319 if (!(item->fState & MF_HILITE))
2321 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2322 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2324 SelectObject( hdc, get_menu_font(FALSE));
2326 item->fState |= MF_HILITE;
2327 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2328 ReleaseDC( menu->hWnd, hdc );
2330 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2331 item->rect = rect;
2333 item->fState |= MF_MOUSESELECT;
2335 if (IS_SYSTEM_MENU(menu))
2337 MENU_InitSysMenuPopup(item->hSubMenu,
2338 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2339 GetClassLongW( menu->hWnd, GCL_STYLE));
2341 NC_GetSysPopupPos( menu->hWnd, &rect );
2342 rect.top = rect.bottom;
2343 rect.right = GetSystemMetrics(SM_CXSIZE);
2344 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2346 else
2348 GetWindowRect( menu->hWnd, &rect );
2349 if (menu->wFlags & MF_POPUP)
2351 RECT rc = item->rect;
2353 MENU_AdjustMenuItemRect(menu, &rc);
2355 /* The first item in the popup menu has to be at the
2356 same y position as the focused menu item */
2357 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2358 rect.top += rc.top - MENU_TOP_MARGIN;
2359 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2360 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2361 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2363 else
2365 rect.left += item->rect.left;
2366 rect.top += item->rect.bottom;
2367 rect.right = item->rect.right - item->rect.left;
2368 rect.bottom = item->rect.bottom - item->rect.top;
2372 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2373 rect.left, rect.top, rect.right, rect.bottom );
2374 if (selectFirst)
2375 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2376 return item->hSubMenu;
2381 /**********************************************************************
2382 * MENU_IsMenuActive
2384 HWND MENU_IsMenuActive(void)
2386 return top_popup;
2389 /**********************************************************************
2390 * MENU_EndMenu
2392 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2394 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2396 void MENU_EndMenu( HWND hwnd )
2398 POPUPMENU *menu;
2399 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2400 if (menu && hwnd == menu->hwndOwner) EndMenu();
2403 /***********************************************************************
2404 * MENU_PtMenu
2406 * Walks menu chain trying to find a menu pt maps to.
2408 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2410 POPUPMENU *menu = MENU_GetMenu( hMenu );
2411 UINT item = menu->FocusedItem;
2412 HMENU ret;
2414 /* try subpopup first (if any) */
2415 ret = (item != NO_SELECTED_ITEM &&
2416 (menu->items[item].fType & MF_POPUP) &&
2417 (menu->items[item].fState & MF_MOUSESELECT))
2418 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2420 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2422 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2423 if( menu->wFlags & MF_POPUP )
2425 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2427 else if (ht == HTSYSMENU)
2428 ret = get_win_sys_menu( menu->hWnd );
2429 else if (ht == HTMENU)
2430 ret = GetMenu( menu->hWnd );
2432 return ret;
2435 /***********************************************************************
2436 * MENU_ExecFocusedItem
2438 * Execute a menu item (for instance when user pressed Enter).
2439 * Return the wID of the executed item. Otherwise, -1 indicating
2440 * that no menu item was executed, -2 if a popup is shown;
2441 * Have to receive the flags for the TrackPopupMenu options to avoid
2442 * sending unwanted message.
2445 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2447 MENUITEM *item;
2448 POPUPMENU *menu = MENU_GetMenu( hMenu );
2450 TRACE("%p hmenu=%p\n", pmt, hMenu);
2452 if (!menu || !menu->nItems ||
2453 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2455 item = &menu->items[menu->FocusedItem];
2457 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2459 if (!(item->fType & MF_POPUP))
2461 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2463 /* If TPM_RETURNCMD is set you return the id, but
2464 do not send a message to the owner */
2465 if(!(wFlags & TPM_RETURNCMD))
2467 if( menu->wFlags & MF_SYSMENU )
2468 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2469 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2470 else
2472 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2473 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2475 if (dwStyle & MNS_NOTIFYBYPOS)
2476 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2477 (LPARAM)hMenu);
2478 else
2479 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2482 return item->wID;
2485 else
2487 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2488 return -2;
2491 return -1;
2494 /***********************************************************************
2495 * MENU_SwitchTracking
2497 * Helper function for menu navigation routines.
2499 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2501 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2502 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2504 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2506 if( pmt->hTopMenu != hPtMenu &&
2507 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2509 /* both are top level menus (system and menu-bar) */
2510 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2511 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2512 pmt->hTopMenu = hPtMenu;
2514 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2515 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2519 /***********************************************************************
2520 * MENU_ButtonDown
2522 * Return TRUE if we can go on with menu tracking.
2524 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2526 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2528 if (hPtMenu)
2530 UINT id = 0;
2531 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2532 MENUITEM *item;
2534 if( IS_SYSTEM_MENU(ptmenu) )
2535 item = ptmenu->items;
2536 else
2537 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2539 if( item )
2541 if( ptmenu->FocusedItem != id )
2542 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2544 /* If the popup menu is not already "popped" */
2545 if(!(item->fState & MF_MOUSESELECT ))
2547 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2550 return TRUE;
2552 /* Else the click was on the menu bar, finish the tracking */
2554 return FALSE;
2557 /***********************************************************************
2558 * MENU_ButtonUp
2560 * Return the value of MENU_ExecFocusedItem if
2561 * the selected item was not a popup. Else open the popup.
2562 * A -1 return value indicates that we go on with menu tracking.
2565 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2567 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2569 if (hPtMenu)
2571 UINT id = 0;
2572 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2573 MENUITEM *item;
2575 if( IS_SYSTEM_MENU(ptmenu) )
2576 item = ptmenu->items;
2577 else
2578 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2580 if( item && (ptmenu->FocusedItem == id ))
2582 debug_print_menuitem ("FocusedItem: ", item, "");
2584 if( !(item->fType & MF_POPUP) )
2586 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2587 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2588 return executedMenuId;
2591 /* If we are dealing with the top-level menu */
2592 /* and this is a click on an already "popped" item: */
2593 /* Stop the menu tracking and close the opened submenus */
2594 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2595 return 0;
2597 ptmenu->bTimeToHide = TRUE;
2599 return -1;
2603 /***********************************************************************
2604 * MENU_MouseMove
2606 * Return TRUE if we can go on with menu tracking.
2608 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2610 UINT id = NO_SELECTED_ITEM;
2611 POPUPMENU *ptmenu = NULL;
2613 if( hPtMenu )
2615 ptmenu = MENU_GetMenu( hPtMenu );
2616 if( IS_SYSTEM_MENU(ptmenu) )
2617 id = 0;
2618 else
2619 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2622 if( id == NO_SELECTED_ITEM )
2624 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2625 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2628 else if( ptmenu->FocusedItem != id )
2630 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2631 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2633 return TRUE;
2637 /***********************************************************************
2638 * MENU_DoNextMenu
2640 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2642 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2644 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2645 BOOL atEnd = FALSE;
2647 /* When skipping left, we need to do something special after the
2648 first menu. */
2649 if (vk == VK_LEFT && menu->FocusedItem == 0)
2651 atEnd = TRUE;
2653 /* When skipping right, for the non-system menu, we need to
2654 handle the last non-special menu item (ie skip any window
2655 icons such as MDI maximize, restore or close) */
2656 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2658 UINT i = menu->FocusedItem + 1;
2659 while (i < menu->nItems) {
2660 if ((menu->items[i].wID >= SC_SIZE &&
2661 menu->items[i].wID <= SC_RESTORE)) {
2662 i++;
2663 } else break;
2665 if (i == menu->nItems) {
2666 atEnd = TRUE;
2669 /* When skipping right, we need to cater for the system menu */
2670 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2672 if (menu->FocusedItem == (menu->nItems - 1)) {
2673 atEnd = TRUE;
2677 if( atEnd )
2679 MDINEXTMENU next_menu;
2680 HMENU hNewMenu;
2681 HWND hNewWnd;
2682 UINT id = 0;
2684 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2685 next_menu.hmenuNext = 0;
2686 next_menu.hwndNext = 0;
2687 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2689 TRACE("%p [%p] -> %p [%p]\n",
2690 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2692 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2694 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2695 hNewWnd = pmt->hOwnerWnd;
2696 if( IS_SYSTEM_MENU(menu) )
2698 /* switch to the menu bar */
2700 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2702 if( vk == VK_LEFT )
2704 menu = MENU_GetMenu( hNewMenu );
2705 id = menu->nItems - 1;
2707 /* Skip backwards over any system predefined icons,
2708 eg. MDI close, restore etc icons */
2709 while ((id > 0) &&
2710 (menu->items[id].wID >= SC_SIZE &&
2711 menu->items[id].wID <= SC_RESTORE)) id--;
2714 else if (style & WS_SYSMENU )
2716 /* switch to the system menu */
2717 hNewMenu = get_win_sys_menu( hNewWnd );
2719 else return FALSE;
2721 else /* application returned a new menu to switch to */
2723 hNewMenu = next_menu.hmenuNext;
2724 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2726 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2728 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2730 if (style & WS_SYSMENU &&
2731 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2733 /* get the real system menu */
2734 hNewMenu = get_win_sys_menu(hNewWnd);
2736 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2738 /* FIXME: Not sure what to do here;
2739 * perhaps try to track hNewMenu as a popup? */
2741 TRACE(" -- got confused.\n");
2742 return FALSE;
2745 else return FALSE;
2748 if( hNewMenu != pmt->hTopMenu )
2750 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2751 FALSE, 0 );
2752 if( pmt->hCurrentMenu != pmt->hTopMenu )
2753 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2756 if( hNewWnd != pmt->hOwnerWnd )
2758 pmt->hOwnerWnd = hNewWnd;
2759 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2762 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2763 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2765 return TRUE;
2767 return FALSE;
2770 /***********************************************************************
2771 * MENU_SuspendPopup
2773 * The idea is not to show the popup if the next input message is
2774 * going to hide it anyway.
2776 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2778 MSG msg;
2780 msg.hwnd = pmt->hOwnerWnd;
2782 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2783 pmt->trackFlags |= TF_SKIPREMOVE;
2785 switch( uMsg )
2787 case WM_KEYDOWN:
2788 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2789 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2791 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2792 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2793 if( msg.message == WM_KEYDOWN &&
2794 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2796 pmt->trackFlags |= TF_SUSPENDPOPUP;
2797 return TRUE;
2800 break;
2803 /* failures go through this */
2804 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2805 return FALSE;
2808 /***********************************************************************
2809 * MENU_KeyEscape
2811 * Handle a VK_ESCAPE key event in a menu.
2813 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2815 BOOL bEndMenu = TRUE;
2817 if (pmt->hCurrentMenu != pmt->hTopMenu)
2819 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2821 if (menu->wFlags & MF_POPUP)
2823 HMENU hmenutmp, hmenuprev;
2825 hmenuprev = hmenutmp = pmt->hTopMenu;
2827 /* close topmost popup */
2828 while (hmenutmp != pmt->hCurrentMenu)
2830 hmenuprev = hmenutmp;
2831 hmenutmp = MENU_GetSubPopup( hmenuprev );
2834 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2835 pmt->hCurrentMenu = hmenuprev;
2836 bEndMenu = FALSE;
2840 return bEndMenu;
2843 /***********************************************************************
2844 * MENU_KeyLeft
2846 * Handle a VK_LEFT key event in a menu.
2848 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2850 POPUPMENU *menu;
2851 HMENU hmenutmp, hmenuprev;
2852 UINT prevcol;
2854 hmenuprev = hmenutmp = pmt->hTopMenu;
2855 menu = MENU_GetMenu( hmenutmp );
2857 /* Try to move 1 column left (if possible) */
2858 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2859 NO_SELECTED_ITEM ) {
2861 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2862 prevcol, TRUE, 0 );
2863 return;
2866 /* close topmost popup */
2867 while (hmenutmp != pmt->hCurrentMenu)
2869 hmenuprev = hmenutmp;
2870 hmenutmp = MENU_GetSubPopup( hmenuprev );
2873 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2874 pmt->hCurrentMenu = hmenuprev;
2876 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2878 /* move menu bar selection if no more popups are left */
2880 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2881 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2883 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2885 /* A sublevel menu was displayed - display the next one
2886 * unless there is another displacement coming up */
2888 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2889 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2890 pmt->hTopMenu, TRUE, wFlags);
2896 /***********************************************************************
2897 * MENU_KeyRight
2899 * Handle a VK_RIGHT key event in a menu.
2901 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2903 HMENU hmenutmp;
2904 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2905 UINT nextcol;
2907 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2908 pmt->hCurrentMenu,
2909 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2910 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2912 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2914 /* If already displaying a popup, try to display sub-popup */
2916 hmenutmp = pmt->hCurrentMenu;
2917 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2919 /* if subpopup was displayed then we are done */
2920 if (hmenutmp != pmt->hCurrentMenu) return;
2923 /* Check to see if there's another column */
2924 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2925 NO_SELECTED_ITEM ) {
2926 TRACE("Going to %d.\n", nextcol );
2927 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2928 nextcol, TRUE, 0 );
2929 return;
2932 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2934 if( pmt->hCurrentMenu != pmt->hTopMenu )
2936 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2937 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2938 } else hmenutmp = 0;
2940 /* try to move to the next item */
2941 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2942 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2944 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2945 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2946 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2947 pmt->hTopMenu, TRUE, wFlags);
2951 static void CALLBACK release_capture( BOOL __normal )
2953 set_capture_window( 0, GUI_INMENUMODE, NULL );
2956 /***********************************************************************
2957 * MENU_TrackMenu
2959 * Menu tracking code.
2961 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2962 HWND hwnd, const RECT *lprect )
2964 MSG msg;
2965 POPUPMENU *menu;
2966 BOOL fRemove;
2967 INT executedMenuId = -1;
2968 MTRACKER mt;
2969 BOOL enterIdleSent = FALSE;
2970 HWND capture_win;
2972 mt.trackFlags = 0;
2973 mt.hCurrentMenu = hmenu;
2974 mt.hTopMenu = hmenu;
2975 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2976 mt.pt.x = x;
2977 mt.pt.y = y;
2979 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2980 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2982 fEndMenu = FALSE;
2983 if (!(menu = MENU_GetMenu( hmenu )))
2985 WARN("Invalid menu handle %p\n", hmenu);
2986 SetLastError(ERROR_INVALID_MENU_HANDLE);
2987 return FALSE;
2990 if (wFlags & TPM_BUTTONDOWN)
2992 /* Get the result in order to start the tracking or not */
2993 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2994 fEndMenu = !fRemove;
2997 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2999 /* owner may not be visible when tracking a popup, so use the menu itself */
3000 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3001 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3003 __TRY while (!fEndMenu)
3005 menu = MENU_GetMenu( mt.hCurrentMenu );
3006 if (!menu) /* sometimes happens if I do a window manager close */
3007 break;
3009 /* we have to keep the message in the queue until it's
3010 * clear that menu loop is not over yet. */
3012 for (;;)
3014 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3016 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3017 /* remove the message from the queue */
3018 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3020 else
3022 if (!enterIdleSent)
3024 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3025 enterIdleSent = TRUE;
3026 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3028 WaitMessage();
3032 /* check if EndMenu() tried to cancel us, by posting this message */
3033 if(msg.message == WM_CANCELMODE)
3035 /* we are now out of the loop */
3036 fEndMenu = TRUE;
3038 /* remove the message from the queue */
3039 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3041 /* break out of internal loop, ala ESCAPE */
3042 break;
3045 TranslateMessage( &msg );
3046 mt.pt = msg.pt;
3048 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3049 enterIdleSent=FALSE;
3051 fRemove = FALSE;
3052 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3055 * Use the mouse coordinates in lParam instead of those in the MSG
3056 * struct to properly handle synthetic messages. They are already
3057 * in screen coordinates.
3059 mt.pt.x = (short)LOWORD(msg.lParam);
3060 mt.pt.y = (short)HIWORD(msg.lParam);
3062 /* Find a menu for this mouse event */
3063 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3065 switch(msg.message)
3067 /* no WM_NC... messages in captured state */
3069 case WM_RBUTTONDBLCLK:
3070 case WM_RBUTTONDOWN:
3071 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3072 /* fall through */
3073 case WM_LBUTTONDBLCLK:
3074 case WM_LBUTTONDOWN:
3075 /* If the message belongs to the menu, removes it from the queue */
3076 /* Else, end menu tracking */
3077 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3078 fEndMenu = !fRemove;
3079 break;
3081 case WM_RBUTTONUP:
3082 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3083 /* fall through */
3084 case WM_LBUTTONUP:
3085 /* Check if a menu was selected by the mouse */
3086 if (hmenu)
3088 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3089 TRACE("executedMenuId %d\n", executedMenuId);
3091 /* End the loop if executedMenuId is an item ID */
3092 /* or if the job was done (executedMenuId = 0). */
3093 fEndMenu = fRemove = (executedMenuId != -1);
3095 /* No menu was selected by the mouse */
3096 /* if the function was called by TrackPopupMenu, continue
3097 with the menu tracking. If not, stop it */
3098 else
3099 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3101 break;
3103 case WM_MOUSEMOVE:
3104 /* the selected menu item must be changed every time */
3105 /* the mouse moves. */
3107 if (hmenu)
3108 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3110 } /* switch(msg.message) - mouse */
3112 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3114 fRemove = TRUE; /* Keyboard messages are always removed */
3115 switch(msg.message)
3117 case WM_KEYDOWN:
3118 case WM_SYSKEYDOWN:
3119 switch(msg.wParam)
3121 case VK_MENU:
3122 case VK_F10:
3123 fEndMenu = TRUE;
3124 break;
3126 case VK_HOME:
3127 case VK_END:
3128 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3129 NO_SELECTED_ITEM, FALSE, 0 );
3130 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3131 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3132 break;
3134 case VK_UP:
3135 case VK_DOWN: /* If on menu bar, pull-down the menu */
3137 menu = MENU_GetMenu( mt.hCurrentMenu );
3138 if (!(menu->wFlags & MF_POPUP))
3139 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3140 else /* otherwise try to move selection */
3141 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3142 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3143 break;
3145 case VK_LEFT:
3146 MENU_KeyLeft( &mt, wFlags );
3147 break;
3149 case VK_RIGHT:
3150 MENU_KeyRight( &mt, wFlags );
3151 break;
3153 case VK_ESCAPE:
3154 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3155 break;
3157 case VK_F1:
3159 HELPINFO hi;
3160 hi.cbSize = sizeof(HELPINFO);
3161 hi.iContextType = HELPINFO_MENUITEM;
3162 if (menu->FocusedItem == NO_SELECTED_ITEM)
3163 hi.iCtrlId = 0;
3164 else
3165 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3166 hi.hItemHandle = hmenu;
3167 hi.dwContextId = menu->dwContextHelpID;
3168 hi.MousePos = msg.pt;
3169 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3170 break;
3173 default:
3174 break;
3176 break; /* WM_KEYDOWN */
3178 case WM_CHAR:
3179 case WM_SYSCHAR:
3181 UINT pos;
3183 if (msg.wParam == '\r' || msg.wParam == ' ')
3185 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3186 fEndMenu = (executedMenuId != -2);
3188 break;
3191 /* Hack to avoid control chars. */
3192 /* We will find a better way real soon... */
3193 if (msg.wParam < 32) break;
3195 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3196 LOWORD(msg.wParam), FALSE );
3197 if (pos == (UINT)-2) fEndMenu = TRUE;
3198 else if (pos == (UINT)-1) MessageBeep(0);
3199 else
3201 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3202 TRUE, 0 );
3203 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3204 fEndMenu = (executedMenuId != -2);
3207 break;
3208 } /* switch(msg.message) - kbd */
3210 else
3212 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3213 DispatchMessageW( &msg );
3214 continue;
3217 if (!fEndMenu) fRemove = TRUE;
3219 /* finally remove message from the queue */
3221 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3222 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3223 else mt.trackFlags &= ~TF_SKIPREMOVE;
3225 __FINALLY( release_capture )
3227 /* If dropdown is still painted and the close box is clicked on
3228 then the menu will be destroyed as part of the DispatchMessage above.
3229 This will then invalidate the menu handle in mt.hTopMenu. We should
3230 check for this first. */
3231 if( IsMenu( mt.hTopMenu ) )
3233 menu = MENU_GetMenu( mt.hTopMenu );
3235 if( IsWindow( mt.hOwnerWnd ) )
3237 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3239 if (menu && (menu->wFlags & MF_POPUP))
3241 DestroyWindow( menu->hWnd );
3242 menu->hWnd = 0;
3244 if (!(wFlags & TPM_NONOTIFY))
3245 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3246 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3248 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3249 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3252 /* Reset the variable for hiding menu */
3253 if( menu ) menu->bTimeToHide = FALSE;
3256 /* The return value is only used by TrackPopupMenu */
3257 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3258 if (executedMenuId == -1) executedMenuId = 0;
3259 return executedMenuId;
3262 /***********************************************************************
3263 * MENU_InitTracking
3265 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3267 POPUPMENU *menu;
3269 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3271 HideCaret(0);
3273 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3274 if (!(wFlags & TPM_NONOTIFY))
3275 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3277 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3279 if (!(wFlags & TPM_NONOTIFY))
3281 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3282 /* If an app changed/recreated menu bar entries in WM_INITMENU
3283 * menu sizes will be recalculated once the menu created/shown.
3287 /* This makes the menus of applications built with Delphi work.
3288 * It also enables menus to be displayed in more than one window,
3289 * but there are some bugs left that need to be fixed in this case.
3291 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3293 return TRUE;
3295 /***********************************************************************
3296 * MENU_ExitTracking
3298 static BOOL MENU_ExitTracking(HWND hWnd)
3300 TRACE("hwnd=%p\n", hWnd);
3302 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3303 ShowCaret(0);
3304 top_popup = 0;
3305 top_popup_hmenu = NULL;
3306 return TRUE;
3309 /***********************************************************************
3310 * MENU_TrackMouseMenuBar
3312 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3314 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3316 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3317 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3319 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3321 if (IsMenu(hMenu))
3323 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3324 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3325 MENU_ExitTracking(hWnd);
3330 /***********************************************************************
3331 * MENU_TrackKbdMenuBar
3333 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3335 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3337 UINT uItem = NO_SELECTED_ITEM;
3338 HMENU hTrackMenu;
3339 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3341 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3343 /* find window that has a menu */
3345 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3346 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3348 /* check if we have to track a system menu */
3350 hTrackMenu = GetMenu( hwnd );
3351 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3353 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3354 hTrackMenu = get_win_sys_menu( hwnd );
3355 uItem = 0;
3356 wParam |= HTSYSMENU; /* prevent item lookup */
3359 if (!IsMenu( hTrackMenu )) return;
3361 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3363 if( wChar && wChar != ' ' )
3365 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3366 if ( uItem >= (UINT)(-2) )
3368 if( uItem == (UINT)(-1) ) MessageBeep(0);
3369 /* schedule end of menu tracking */
3370 wFlags |= TF_ENDMENU;
3371 goto track_menu;
3375 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3377 if (!(wParam & HTSYSMENU) || wChar == ' ')
3379 if( uItem == NO_SELECTED_ITEM )
3380 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3381 else
3382 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3385 track_menu:
3386 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3387 MENU_ExitTracking( hwnd );
3390 /**********************************************************************
3391 * TrackPopupMenuEx (USER32.@)
3393 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3394 HWND hWnd, LPTPMPARAMS lpTpm )
3396 BOOL ret = FALSE;
3398 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3399 hMenu, wFlags, x, y, hWnd, lpTpm,
3400 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3402 /* Parameter check */
3403 /* FIXME: this check is performed several times, here and in the called
3404 functions. That could be optimized */
3405 if (!MENU_GetMenu( hMenu ))
3407 SetLastError( ERROR_INVALID_MENU_HANDLE );
3408 return FALSE;
3411 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3413 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3414 if (!(wFlags & TPM_NONOTIFY))
3415 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3417 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3418 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3419 lpTpm ? &lpTpm->rcExclude : NULL );
3420 MENU_ExitTracking(hWnd);
3422 return ret;
3425 /**********************************************************************
3426 * TrackPopupMenu (USER32.@)
3428 * Like the win32 API, the function return the command ID only if the
3429 * flag TPM_RETURNCMD is on.
3432 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3433 INT nReserved, HWND hWnd, const RECT *lpRect )
3435 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3438 /***********************************************************************
3439 * PopupMenuWndProc
3441 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3443 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3445 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3447 switch(message)
3449 case WM_CREATE:
3451 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3452 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3453 return 0;
3456 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3457 return MA_NOACTIVATE;
3459 case WM_PAINT:
3461 PAINTSTRUCT ps;
3462 BeginPaint( hwnd, &ps );
3463 MENU_DrawPopupMenu( hwnd, ps.hdc,
3464 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3465 EndPaint( hwnd, &ps );
3466 return 0;
3469 case WM_PRINTCLIENT:
3471 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3472 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3473 return 0;
3476 case WM_ERASEBKGND:
3477 return 1;
3479 case WM_DESTROY:
3480 /* zero out global pointer in case resident popup window was destroyed. */
3481 if (hwnd == top_popup) {
3482 top_popup = 0;
3483 top_popup_hmenu = NULL;
3485 break;
3487 case WM_SHOWWINDOW:
3489 if( wParam )
3491 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3493 else
3494 SetWindowLongPtrW( hwnd, 0, 0 );
3495 break;
3497 case MM_SETMENUHANDLE:
3498 SetWindowLongPtrW( hwnd, 0, wParam );
3499 break;
3501 case MM_GETMENUHANDLE:
3502 case MN_GETHMENU:
3503 return GetWindowLongPtrW( hwnd, 0 );
3505 default:
3506 return DefWindowProcW( hwnd, message, wParam, lParam );
3508 return 0;
3512 /***********************************************************************
3513 * MENU_GetMenuBarHeight
3515 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3517 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3518 INT orgX, INT orgY )
3520 HDC hdc;
3521 RECT rectBar;
3522 LPPOPUPMENU lppop;
3524 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3526 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3528 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3529 SelectObject( hdc, get_menu_font(FALSE));
3530 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3531 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3532 ReleaseDC( hwnd, hdc );
3533 return lppop->Height;
3537 /*******************************************************************
3538 * ChangeMenuA (USER32.@)
3540 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3541 UINT id, UINT flags )
3543 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3544 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3545 id, data );
3546 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3547 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3548 id, data );
3549 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3550 flags & MF_BYPOSITION ? pos : id,
3551 flags & ~MF_REMOVE );
3552 /* Default: MF_INSERT */
3553 return InsertMenuA( hMenu, pos, flags, id, data );
3557 /*******************************************************************
3558 * ChangeMenuW (USER32.@)
3560 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3561 UINT id, UINT flags )
3563 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3564 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3565 id, data );
3566 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3567 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3568 id, data );
3569 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3570 flags & MF_BYPOSITION ? pos : id,
3571 flags & ~MF_REMOVE );
3572 /* Default: MF_INSERT */
3573 return InsertMenuW( hMenu, pos, flags, id, data );
3577 /*******************************************************************
3578 * CheckMenuItem (USER32.@)
3580 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3582 MENUITEM *item;
3583 DWORD ret;
3585 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3586 ret = item->fState & MF_CHECKED;
3587 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3588 else item->fState &= ~MF_CHECKED;
3589 return ret;
3593 /**********************************************************************
3594 * EnableMenuItem (USER32.@)
3596 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3598 UINT oldflags;
3599 MENUITEM *item;
3600 POPUPMENU *menu;
3602 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3604 /* Get the Popupmenu to access the owner menu */
3605 if (!(menu = MENU_GetMenu(hMenu)))
3606 return (UINT)-1;
3608 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3609 return (UINT)-1;
3611 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3612 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3614 /* If the close item in the system menu change update the close button */
3615 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3617 if (menu->hSysMenuOwner != 0)
3619 RECT rc;
3620 POPUPMENU* parentMenu;
3622 /* Get the parent menu to access*/
3623 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3624 return (UINT)-1;
3626 /* Refresh the frame to reflect the change */
3627 GetWindowRect(parentMenu->hWnd, &rc);
3628 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3629 rc.bottom = 0;
3630 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3634 return oldflags;
3638 /*******************************************************************
3639 * GetMenuStringA (USER32.@)
3641 INT WINAPI GetMenuStringA(
3642 HMENU hMenu, /* [in] menuhandle */
3643 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3644 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3645 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3646 UINT wFlags /* [in] MF_ flags */
3648 MENUITEM *item;
3650 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3651 if (str && nMaxSiz) str[0] = '\0';
3652 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3653 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3654 return 0;
3656 if (!item->text) return 0;
3657 if (!str || !nMaxSiz) return strlenW(item->text);
3658 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3659 str[nMaxSiz-1] = 0;
3660 TRACE("returning %s\n", debugstr_a(str));
3661 return strlen(str);
3665 /*******************************************************************
3666 * GetMenuStringW (USER32.@)
3668 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3669 LPWSTR str, INT nMaxSiz, UINT wFlags )
3671 MENUITEM *item;
3673 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3674 if (str && nMaxSiz) str[0] = '\0';
3675 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3676 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3677 return 0;
3679 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3680 if( !(item->text)) {
3681 str[0] = 0;
3682 return 0;
3684 lstrcpynW( str, item->text, nMaxSiz );
3685 TRACE("returning %s\n", debugstr_w(str));
3686 return strlenW(str);
3690 /**********************************************************************
3691 * HiliteMenuItem (USER32.@)
3693 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3694 UINT wHilite )
3696 LPPOPUPMENU menu;
3697 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3698 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3699 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3700 if (menu->FocusedItem == wItemID) return TRUE;
3701 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3702 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3703 return TRUE;
3707 /**********************************************************************
3708 * GetMenuState (USER32.@)
3710 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3712 MENUITEM *item;
3713 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3714 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3715 debug_print_menuitem (" item: ", item, "");
3716 if (item->fType & MF_POPUP)
3718 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3719 if (!menu) return -1;
3720 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3722 else
3724 /* We used to (from way back then) mask the result to 0xff. */
3725 /* I don't know why and it seems wrong as the documented */
3726 /* return flag MF_SEPARATOR is outside that mask. */
3727 return (item->fType | item->fState);
3732 /**********************************************************************
3733 * GetMenuItemCount (USER32.@)
3735 INT WINAPI GetMenuItemCount( HMENU hMenu )
3737 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3738 if (!menu) return -1;
3739 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3740 return menu->nItems;
3744 /**********************************************************************
3745 * GetMenuItemID (USER32.@)
3747 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3749 MENUITEM * lpmi;
3751 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3752 if (lpmi->fType & MF_POPUP) return -1;
3753 return lpmi->wID;
3758 /**********************************************************************
3759 * MENU_mnu2mnuii
3761 * Uses flags, id and text ptr, passed by InsertMenu() and
3762 * ModifyMenu() to setup a MenuItemInfo structure.
3764 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3765 LPMENUITEMINFOW pmii)
3767 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3768 pmii->cbSize = sizeof( MENUITEMINFOW);
3769 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3770 /* setting bitmap clears text and vice versa */
3771 if( IS_STRING_ITEM(flags)) {
3772 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3773 if( !str)
3774 flags |= MF_SEPARATOR;
3775 /* Item beginning with a backspace is a help item */
3776 /* FIXME: wrong place, this is only true in win16 */
3777 else if( *str == '\b') {
3778 flags |= MF_HELP;
3779 str++;
3781 pmii->dwTypeData = (LPWSTR)str;
3782 } else if( flags & MFT_BITMAP){
3783 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3784 pmii->hbmpItem = ULongToHandle(LOWORD(str));
3786 if( flags & MF_OWNERDRAW){
3787 pmii->fMask |= MIIM_DATA;
3788 pmii->dwItemData = (ULONG_PTR) str;
3790 if( flags & MF_POPUP) {
3791 pmii->fMask |= MIIM_SUBMENU;
3792 pmii->hSubMenu = (HMENU)id;
3794 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3795 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3796 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3797 pmii->wID = (UINT)id;
3801 /*******************************************************************
3802 * InsertMenuW (USER32.@)
3804 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3805 UINT_PTR id, LPCWSTR str )
3807 MENUITEM *item;
3808 MENUITEMINFOW mii;
3810 if (IS_STRING_ITEM(flags) && str)
3811 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3812 hMenu, pos, flags, id, debugstr_w(str) );
3813 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3814 hMenu, pos, flags, id, str );
3816 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3817 MENU_mnu2mnuii( flags, id, str, &mii);
3818 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3820 RemoveMenu( hMenu, pos, flags );
3821 return FALSE;
3824 item->hCheckBit = item->hUnCheckBit = 0;
3825 return TRUE;
3829 /*******************************************************************
3830 * InsertMenuA (USER32.@)
3832 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3833 UINT_PTR id, LPCSTR str )
3835 BOOL ret = FALSE;
3837 if (IS_STRING_ITEM(flags) && str)
3839 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3840 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3841 if (newstr)
3843 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3844 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3845 HeapFree( GetProcessHeap(), 0, newstr );
3847 return ret;
3849 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3853 /*******************************************************************
3854 * AppendMenuA (USER32.@)
3856 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3857 UINT_PTR id, LPCSTR data )
3859 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3863 /*******************************************************************
3864 * AppendMenuW (USER32.@)
3866 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3867 UINT_PTR id, LPCWSTR data )
3869 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3873 /**********************************************************************
3874 * RemoveMenu (USER32.@)
3876 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3878 LPPOPUPMENU menu;
3879 MENUITEM *item;
3881 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3882 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3883 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3885 /* Remove item */
3887 MENU_FreeItemData( item );
3889 if (--menu->nItems == 0)
3891 HeapFree( GetProcessHeap(), 0, menu->items );
3892 menu->items = NULL;
3894 else
3896 while(nPos < menu->nItems)
3898 *item = *(item+1);
3899 item++;
3900 nPos++;
3902 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3903 menu->nItems * sizeof(MENUITEM) );
3905 return TRUE;
3909 /**********************************************************************
3910 * DeleteMenu (USER32.@)
3912 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3914 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3915 if (!item) return FALSE;
3916 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3917 /* nPos is now the position of the item */
3918 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3919 return TRUE;
3923 /*******************************************************************
3924 * ModifyMenuW (USER32.@)
3926 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3927 UINT_PTR id, LPCWSTR str )
3929 MENUITEM *item;
3930 MENUITEMINFOW mii;
3932 if (IS_STRING_ITEM(flags))
3933 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3934 else
3935 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3937 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3938 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3939 MENU_mnu2mnuii( flags, id, str, &mii);
3940 return SetMenuItemInfo_common( item, &mii, TRUE);
3944 /*******************************************************************
3945 * ModifyMenuA (USER32.@)
3947 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3948 UINT_PTR id, LPCSTR str )
3950 BOOL ret = FALSE;
3952 if (IS_STRING_ITEM(flags) && str)
3954 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3955 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3956 if (newstr)
3958 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3959 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3960 HeapFree( GetProcessHeap(), 0, newstr );
3962 return ret;
3964 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3968 /**********************************************************************
3969 * CreatePopupMenu (USER32.@)
3971 HMENU WINAPI CreatePopupMenu(void)
3973 HMENU hmenu;
3974 POPUPMENU *menu;
3976 if (!(hmenu = CreateMenu())) return 0;
3977 menu = MENU_GetMenu( hmenu );
3978 menu->wFlags |= MF_POPUP;
3979 menu->bTimeToHide = FALSE;
3980 return hmenu;
3984 /**********************************************************************
3985 * GetMenuCheckMarkDimensions (USER.417)
3986 * GetMenuCheckMarkDimensions (USER32.@)
3988 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3990 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3994 /**********************************************************************
3995 * SetMenuItemBitmaps (USER32.@)
3997 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3998 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4000 MENUITEM *item;
4002 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4004 if (!hNewCheck && !hNewUnCheck)
4006 item->fState &= ~MF_USECHECKBITMAPS;
4008 else /* Install new bitmaps */
4010 item->hCheckBit = hNewCheck;
4011 item->hUnCheckBit = hNewUnCheck;
4012 item->fState |= MF_USECHECKBITMAPS;
4014 return TRUE;
4018 /**********************************************************************
4019 * CreateMenu (USER32.@)
4021 HMENU WINAPI CreateMenu(void)
4023 HMENU hMenu;
4024 LPPOPUPMENU menu;
4026 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4027 menu->FocusedItem = NO_SELECTED_ITEM;
4028 menu->bTimeToHide = FALSE;
4030 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4032 TRACE("return %p\n", hMenu );
4034 return hMenu;
4038 /**********************************************************************
4039 * DestroyMenu (USER32.@)
4041 BOOL WINAPI DestroyMenu( HMENU hMenu )
4043 LPPOPUPMENU lppop;
4045 TRACE("(%p)\n", hMenu);
4047 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4048 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4050 /* DestroyMenu should not destroy system menu popup owner */
4051 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4053 DestroyWindow( lppop->hWnd );
4054 lppop->hWnd = 0;
4057 if (lppop->items) /* recursively destroy submenus */
4059 int i;
4060 MENUITEM *item = lppop->items;
4061 for (i = lppop->nItems; i > 0; i--, item++)
4063 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4064 MENU_FreeItemData( item );
4066 HeapFree( GetProcessHeap(), 0, lppop->items );
4068 HeapFree( GetProcessHeap(), 0, lppop );
4069 return TRUE;
4073 /**********************************************************************
4074 * GetSystemMenu (USER32.@)
4076 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4078 WND *wndPtr = WIN_GetPtr( hWnd );
4079 HMENU retvalue = 0;
4081 if (wndPtr == WND_DESKTOP) return 0;
4082 if (wndPtr == WND_OTHER_PROCESS)
4084 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4086 else if (wndPtr)
4088 if (wndPtr->hSysMenu && bRevert)
4090 DestroyMenu(wndPtr->hSysMenu);
4091 wndPtr->hSysMenu = 0;
4094 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4095 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4097 if( wndPtr->hSysMenu )
4099 POPUPMENU *menu;
4100 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4102 /* Store the dummy sysmenu handle to facilitate the refresh */
4103 /* of the close button if the SC_CLOSE item change */
4104 menu = MENU_GetMenu(retvalue);
4105 if ( menu )
4106 menu->hSysMenuOwner = wndPtr->hSysMenu;
4108 WIN_ReleasePtr( wndPtr );
4110 return bRevert ? 0 : retvalue;
4114 /*******************************************************************
4115 * SetSystemMenu (USER32.@)
4117 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4119 WND *wndPtr = WIN_GetPtr( hwnd );
4121 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4123 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4124 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4125 WIN_ReleasePtr( wndPtr );
4126 return TRUE;
4128 return FALSE;
4132 /**********************************************************************
4133 * GetMenu (USER32.@)
4135 HMENU WINAPI GetMenu( HWND hWnd )
4137 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4138 TRACE("for %p returning %p\n", hWnd, retvalue);
4139 return retvalue;
4142 /**********************************************************************
4143 * GetMenuBarInfo (USER32.@)
4145 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4147 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4148 return FALSE;
4151 /**********************************************************************
4152 * MENU_SetMenu
4154 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4155 * SetWindowPos call that would result if SetMenu were called directly.
4157 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4159 TRACE("(%p, %p);\n", hWnd, hMenu);
4161 if (hMenu && !IsMenu(hMenu))
4163 WARN("hMenu %p is not a menu handle\n", hMenu);
4164 return FALSE;
4166 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4167 return FALSE;
4169 hWnd = WIN_GetFullHandle( hWnd );
4170 if (GetCapture() == hWnd)
4171 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4173 if (hMenu != 0)
4175 LPPOPUPMENU lpmenu;
4177 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4179 lpmenu->hWnd = hWnd;
4180 lpmenu->Height = 0; /* Make sure we recalculate the size */
4182 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4183 return TRUE;
4187 /**********************************************************************
4188 * SetMenu (USER32.@)
4190 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4192 if(!MENU_SetMenu(hWnd, hMenu))
4193 return FALSE;
4195 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4196 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4197 return TRUE;
4201 /**********************************************************************
4202 * GetSubMenu (USER32.@)
4204 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4206 MENUITEM * lpmi;
4208 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4209 if (!(lpmi->fType & MF_POPUP)) return 0;
4210 return lpmi->hSubMenu;
4214 /**********************************************************************
4215 * DrawMenuBar (USER32.@)
4217 BOOL WINAPI DrawMenuBar( HWND hWnd )
4219 LPPOPUPMENU lppop;
4220 HMENU hMenu = GetMenu(hWnd);
4222 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4223 return FALSE;
4224 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4226 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4227 lppop->hwndOwner = hWnd;
4228 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4229 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4230 return TRUE;
4233 /***********************************************************************
4234 * DrawMenuBarTemp (USER32.@)
4236 * UNDOCUMENTED !!
4238 * called by W98SE desk.cpl Control Panel Applet
4240 * Not 100% sure about the param names, but close.
4242 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4244 LPPOPUPMENU lppop;
4245 UINT i,retvalue;
4246 HFONT hfontOld = 0;
4247 BOOL flat_menu = FALSE;
4249 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4251 if (!hMenu)
4252 hMenu = GetMenu(hwnd);
4254 if (!hFont)
4255 hFont = get_menu_font(FALSE);
4257 lppop = MENU_GetMenu( hMenu );
4258 if (lppop == NULL || lprect == NULL)
4260 retvalue = GetSystemMetrics(SM_CYMENU);
4261 goto END;
4264 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4266 hfontOld = SelectObject( hDC, hFont);
4268 if (lppop->Height == 0)
4269 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4271 lprect->bottom = lprect->top + lppop->Height;
4273 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4275 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4276 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4277 LineTo( hDC, lprect->right, lprect->bottom );
4279 if (lppop->nItems == 0)
4281 retvalue = GetSystemMetrics(SM_CYMENU);
4282 goto END;
4285 for (i = 0; i < lppop->nItems; i++)
4287 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4288 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4290 retvalue = lppop->Height;
4292 END:
4293 if (hfontOld) SelectObject (hDC, hfontOld);
4294 return retvalue;
4297 /***********************************************************************
4298 * EndMenu (USER.187)
4299 * EndMenu (USER32.@)
4301 BOOL WINAPI EndMenu(void)
4303 /* if we are in the menu code, and it is active */
4304 if (!fEndMenu && top_popup)
4306 /* terminate the menu handling code */
4307 fEndMenu = TRUE;
4309 /* needs to be posted to wakeup the internal menu handler */
4310 /* which will now terminate the menu, in the event that */
4311 /* the main window was minimized, or lost focus, so we */
4312 /* don't end up with an orphaned menu */
4313 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4315 return fEndMenu;
4319 /*****************************************************************
4320 * LoadMenuA (USER32.@)
4322 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4324 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4325 if (!hrsrc) return 0;
4326 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4330 /*****************************************************************
4331 * LoadMenuW (USER32.@)
4333 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4335 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4336 if (!hrsrc) return 0;
4337 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4341 /**********************************************************************
4342 * LoadMenuIndirectW (USER32.@)
4344 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4346 HMENU hMenu;
4347 WORD version, offset;
4348 LPCSTR p = template;
4350 version = GET_WORD(p);
4351 p += sizeof(WORD);
4352 TRACE("%p, ver %d\n", template, version );
4353 switch (version)
4355 case 0: /* standard format is version of 0 */
4356 offset = GET_WORD(p);
4357 p += sizeof(WORD) + offset;
4358 if (!(hMenu = CreateMenu())) return 0;
4359 if (!MENU_ParseResource( p, hMenu ))
4361 DestroyMenu( hMenu );
4362 return 0;
4364 return hMenu;
4365 case 1: /* extended format is version of 1 */
4366 offset = GET_WORD(p);
4367 p += sizeof(WORD) + offset;
4368 if (!(hMenu = CreateMenu())) return 0;
4369 if (!MENUEX_ParseResource( p, hMenu))
4371 DestroyMenu( hMenu );
4372 return 0;
4374 return hMenu;
4375 default:
4376 ERR("version %d not supported.\n", version);
4377 return 0;
4382 /**********************************************************************
4383 * LoadMenuIndirectA (USER32.@)
4385 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4387 return LoadMenuIndirectW( template );
4391 /**********************************************************************
4392 * IsMenu (USER32.@)
4394 BOOL WINAPI IsMenu(HMENU hmenu)
4396 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4398 if (!menu)
4400 SetLastError(ERROR_INVALID_MENU_HANDLE);
4401 return FALSE;
4403 return TRUE;
4406 /**********************************************************************
4407 * GetMenuItemInfo_common
4410 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4411 LPMENUITEMINFOW lpmii, BOOL unicode)
4413 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4415 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4417 if (!menu) {
4418 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4419 return FALSE;
4422 if( lpmii->fMask & MIIM_TYPE) {
4423 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4424 WARN("invalid combination of fMask bits used\n");
4425 /* this does not happen on Win9x/ME */
4426 SetLastError( ERROR_INVALID_PARAMETER);
4427 return FALSE;
4429 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4430 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4431 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4432 if( lpmii->fType & MFT_BITMAP) {
4433 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4434 lpmii->cch = 0;
4435 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4436 /* this does not happen on Win9x/ME */
4437 lpmii->dwTypeData = 0;
4438 lpmii->cch = 0;
4442 /* copy the text string */
4443 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4444 if( !menu->text ) {
4445 if(lpmii->dwTypeData && lpmii->cch) {
4446 lpmii->cch = 0;
4447 if( unicode)
4448 *((WCHAR *)lpmii->dwTypeData) = 0;
4449 else
4450 *((CHAR *)lpmii->dwTypeData) = 0;
4452 } else {
4453 int len;
4454 if (unicode)
4456 len = strlenW(menu->text);
4457 if(lpmii->dwTypeData && lpmii->cch)
4458 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4460 else
4462 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4463 0, NULL, NULL ) - 1;
4464 if(lpmii->dwTypeData && lpmii->cch)
4465 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4466 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4467 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4469 /* if we've copied a substring we return its length */
4470 if(lpmii->dwTypeData && lpmii->cch)
4471 if (lpmii->cch <= len + 1)
4472 lpmii->cch--;
4473 else
4474 lpmii->cch = len;
4475 else {
4476 /* return length of string */
4477 /* not on Win9x/ME if fType & MFT_BITMAP */
4478 lpmii->cch = len;
4483 if (lpmii->fMask & MIIM_FTYPE)
4484 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4486 if (lpmii->fMask & MIIM_BITMAP)
4487 lpmii->hbmpItem = menu->hbmpItem;
4489 if (lpmii->fMask & MIIM_STATE)
4490 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4492 if (lpmii->fMask & MIIM_ID)
4493 lpmii->wID = menu->wID;
4495 if (lpmii->fMask & MIIM_SUBMENU)
4496 lpmii->hSubMenu = menu->hSubMenu;
4497 else {
4498 /* hSubMenu is always cleared
4499 * (not on Win9x/ME ) */
4500 lpmii->hSubMenu = 0;
4503 if (lpmii->fMask & MIIM_CHECKMARKS) {
4504 lpmii->hbmpChecked = menu->hCheckBit;
4505 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4507 if (lpmii->fMask & MIIM_DATA)
4508 lpmii->dwItemData = menu->dwItemData;
4510 return TRUE;
4513 /**********************************************************************
4514 * GetMenuItemInfoA (USER32.@)
4516 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4517 LPMENUITEMINFOA lpmii)
4519 BOOL ret;
4520 MENUITEMINFOA mii;
4521 if( lpmii->cbSize != sizeof( mii) &&
4522 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4523 SetLastError( ERROR_INVALID_PARAMETER);
4524 return FALSE;
4526 memcpy( &mii, lpmii, lpmii->cbSize);
4527 mii.cbSize = sizeof( mii);
4528 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4529 (LPMENUITEMINFOW)&mii, FALSE);
4530 mii.cbSize = lpmii->cbSize;
4531 memcpy( lpmii, &mii, mii.cbSize);
4532 return ret;
4535 /**********************************************************************
4536 * GetMenuItemInfoW (USER32.@)
4538 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4539 LPMENUITEMINFOW lpmii)
4541 BOOL ret;
4542 MENUITEMINFOW mii;
4543 if( lpmii->cbSize != sizeof( mii) &&
4544 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4545 SetLastError( ERROR_INVALID_PARAMETER);
4546 return FALSE;
4548 memcpy( &mii, lpmii, lpmii->cbSize);
4549 mii.cbSize = sizeof( mii);
4550 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4551 mii.cbSize = lpmii->cbSize;
4552 memcpy( lpmii, &mii, mii.cbSize);
4553 return ret;
4557 /* set a menu item text from a ASCII or Unicode string */
4558 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4560 if (!text)
4561 menu->text = NULL;
4562 else if (unicode)
4564 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4565 strcpyW( menu->text, text );
4567 else
4569 LPCSTR str = (LPCSTR)text;
4570 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4571 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4572 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4577 /**********************************************************************
4578 * MENU_depth
4580 * detect if there are loops in the menu tree (or the depth is too large)
4582 static int MENU_depth( POPUPMENU *pmenu, int depth)
4584 int i;
4585 MENUITEM *item;
4586 int subdepth;
4588 depth++;
4589 if( depth > MAXMENUDEPTH) return depth;
4590 item = pmenu->items;
4591 subdepth = depth;
4592 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4593 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4594 if( psubmenu){
4595 int bdepth = MENU_depth( psubmenu, depth);
4596 if( bdepth > subdepth) subdepth = bdepth;
4598 if( subdepth > MAXMENUDEPTH)
4599 TRACE("<- hmenu %p\n", item->hSubMenu);
4601 return subdepth;
4605 /**********************************************************************
4606 * SetMenuItemInfo_common
4608 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4609 * MIIM_BITMAP and MIIM_STRING flags instead.
4612 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4613 const MENUITEMINFOW *lpmii,
4614 BOOL unicode)
4616 if (!menu) return FALSE;
4618 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4620 if (lpmii->fMask & MIIM_FTYPE ) {
4621 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4622 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4624 if (lpmii->fMask & MIIM_STRING ) {
4625 /* free the string when used */
4626 HeapFree(GetProcessHeap(), 0, menu->text);
4627 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4630 if (lpmii->fMask & MIIM_STATE)
4631 /* Other menu items having MFS_DEFAULT are not converted
4632 to normal items */
4633 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4635 if (lpmii->fMask & MIIM_ID)
4636 menu->wID = lpmii->wID;
4638 if (lpmii->fMask & MIIM_SUBMENU) {
4639 menu->hSubMenu = lpmii->hSubMenu;
4640 if (menu->hSubMenu) {
4641 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4642 if (subMenu) {
4643 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4644 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4645 menu->hSubMenu = 0;
4646 return FALSE;
4648 subMenu->wFlags |= MF_POPUP;
4649 menu->fType |= MF_POPUP;
4650 } else {
4651 SetLastError( ERROR_INVALID_PARAMETER);
4652 return FALSE;
4655 else
4656 menu->fType &= ~MF_POPUP;
4659 if (lpmii->fMask & MIIM_CHECKMARKS)
4661 menu->hCheckBit = lpmii->hbmpChecked;
4662 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4664 if (lpmii->fMask & MIIM_DATA)
4665 menu->dwItemData = lpmii->dwItemData;
4667 if (lpmii->fMask & MIIM_BITMAP)
4668 menu->hbmpItem = lpmii->hbmpItem;
4670 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4671 menu->fType |= MFT_SEPARATOR;
4673 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4674 return TRUE;
4677 /**********************************************************************
4678 * MENU_NormalizeMenuItemInfoStruct
4680 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4681 * check, copy and extend the MENUITEMINFO struct from the version that the application
4682 * supplied to the version used by wine source. */
4683 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4684 MENUITEMINFOW *pmii_out )
4686 /* do we recognize the size? */
4687 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4688 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4689 SetLastError( ERROR_INVALID_PARAMETER);
4690 return FALSE;
4692 /* copy the fields that we have */
4693 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4694 /* if the hbmpItem member is missing then extend */
4695 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4696 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4697 pmii_out->hbmpItem = NULL;
4699 /* test for invalid bit combinations */
4700 if( (pmii_out->fMask & MIIM_TYPE &&
4701 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4702 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4703 WARN("invalid combination of fMask bits used\n");
4704 /* this does not happen on Win9x/ME */
4705 SetLastError( ERROR_INVALID_PARAMETER);
4706 return FALSE;
4708 /* convert old style (MIIM_TYPE) to the new */
4709 if( pmii_out->fMask & MIIM_TYPE){
4710 pmii_out->fMask |= MIIM_FTYPE;
4711 if( IS_STRING_ITEM(pmii_out->fType)){
4712 pmii_out->fMask |= MIIM_STRING;
4713 } else if( (pmii_out->fType) & MFT_BITMAP){
4714 pmii_out->fMask |= MIIM_BITMAP;
4715 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4718 return TRUE;
4721 /**********************************************************************
4722 * SetMenuItemInfoA (USER32.@)
4724 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4725 const MENUITEMINFOA *lpmii)
4727 MENUITEMINFOW mii;
4729 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4731 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4733 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4734 &mii, FALSE);
4737 /**********************************************************************
4738 * SetMenuItemInfoW (USER32.@)
4740 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4741 const MENUITEMINFOW *lpmii)
4743 MENUITEMINFOW mii;
4745 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4747 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4748 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4749 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4752 /**********************************************************************
4753 * SetMenuDefaultItem (USER32.@)
4756 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4758 UINT i;
4759 POPUPMENU *menu;
4760 MENUITEM *item;
4762 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4764 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4766 /* reset all default-item flags */
4767 item = menu->items;
4768 for (i = 0; i < menu->nItems; i++, item++)
4770 item->fState &= ~MFS_DEFAULT;
4773 /* no default item */
4774 if ( -1 == uItem)
4776 return TRUE;
4779 item = menu->items;
4780 if ( bypos )
4782 if ( uItem >= menu->nItems ) return FALSE;
4783 item[uItem].fState |= MFS_DEFAULT;
4784 return TRUE;
4786 else
4788 for (i = 0; i < menu->nItems; i++, item++)
4790 if (item->wID == uItem)
4792 item->fState |= MFS_DEFAULT;
4793 return TRUE;
4798 return FALSE;
4801 /**********************************************************************
4802 * GetMenuDefaultItem (USER32.@)
4804 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4806 POPUPMENU *menu;
4807 MENUITEM * item;
4808 UINT i = 0;
4810 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4812 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4814 /* find default item */
4815 item = menu->items;
4817 /* empty menu */
4818 if (! item) return -1;
4820 while ( !( item->fState & MFS_DEFAULT ) )
4822 i++; item++;
4823 if (i >= menu->nItems ) return -1;
4826 /* default: don't return disabled items */
4827 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4829 /* search rekursiv when needed */
4830 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4832 UINT ret;
4833 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4834 if ( -1 != ret ) return ret;
4836 /* when item not found in submenu, return the popup item */
4838 return ( bypos ) ? i : item->wID;
4843 /**********************************************************************
4844 * InsertMenuItemA (USER32.@)
4846 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4847 const MENUITEMINFOA *lpmii)
4849 MENUITEM *item;
4850 MENUITEMINFOW mii;
4852 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4854 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4856 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4857 return SetMenuItemInfo_common(item, &mii, FALSE);
4861 /**********************************************************************
4862 * InsertMenuItemW (USER32.@)
4864 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4865 const MENUITEMINFOW *lpmii)
4867 MENUITEM *item;
4868 MENUITEMINFOW mii;
4870 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4872 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4874 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4875 return SetMenuItemInfo_common(item, &mii, TRUE);
4878 /**********************************************************************
4879 * CheckMenuRadioItem (USER32.@)
4882 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4883 UINT first, UINT last, UINT check,
4884 UINT bypos)
4886 BOOL done = FALSE;
4887 UINT i;
4888 MENUITEM *mi_first = NULL, *mi_check;
4889 HMENU m_first, m_check;
4891 for (i = first; i <= last; i++)
4893 UINT pos = i;
4895 if (!mi_first)
4897 m_first = hMenu;
4898 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4899 if (!mi_first) continue;
4900 mi_check = mi_first;
4901 m_check = m_first;
4903 else
4905 m_check = hMenu;
4906 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4907 if (!mi_check) continue;
4910 if (m_first != m_check) continue;
4911 if (mi_check->fType == MFT_SEPARATOR) continue;
4913 if (i == check)
4915 mi_check->fType |= MFT_RADIOCHECK;
4916 mi_check->fState |= MFS_CHECKED;
4917 done = TRUE;
4919 else
4921 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4922 mi_check->fState &= ~MFS_CHECKED;
4926 return done;
4930 /**********************************************************************
4931 * GetMenuItemRect (USER32.@)
4933 * ATTENTION: Here, the returned values in rect are the screen
4934 * coordinates of the item just like if the menu was
4935 * always on the upper left side of the application.
4938 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4939 LPRECT rect)
4941 POPUPMENU *itemMenu;
4942 MENUITEM *item;
4943 HWND referenceHwnd;
4945 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4947 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4948 referenceHwnd = hwnd;
4950 if(!hwnd)
4952 itemMenu = MENU_GetMenu(hMenu);
4953 if (itemMenu == NULL)
4954 return FALSE;
4956 if(itemMenu->hWnd == 0)
4957 return FALSE;
4958 referenceHwnd = itemMenu->hWnd;
4961 if ((rect == NULL) || (item == NULL))
4962 return FALSE;
4964 *rect = item->rect;
4966 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4968 return TRUE;
4971 /**********************************************************************
4972 * SetMenuInfo (USER32.@)
4974 * FIXME
4975 * actually use the items to draw the menu
4976 * (recalculate and/or redraw)
4978 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
4980 POPUPMENU *menu;
4981 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
4983 if (lpmi->fMask & MIM_BACKGROUND)
4984 menu->hbrBack = lpmi->hbrBack;
4986 if (lpmi->fMask & MIM_HELPID)
4987 menu->dwContextHelpID = lpmi->dwContextHelpID;
4989 if (lpmi->fMask & MIM_MAXHEIGHT)
4990 menu->cyMax = lpmi->cyMax;
4992 if (lpmi->fMask & MIM_MENUDATA)
4993 menu->dwMenuData = lpmi->dwMenuData;
4995 if (lpmi->fMask & MIM_STYLE)
4996 menu->dwStyle = lpmi->dwStyle;
4998 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
4999 int i;
5000 MENUITEM *item = menu->items;
5001 for( i = menu->nItems; i; i--, item++)
5002 if( item->fType & MF_POPUP)
5003 menu_SetMenuInfo( item->hSubMenu, lpmi);
5005 return TRUE;
5008 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5010 TRACE("(%p %p)\n", hMenu, lpmi);
5011 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5012 if( lpmi->fMask & MIM_STYLE) {
5013 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5014 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5015 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5017 return TRUE;
5019 SetLastError( ERROR_INVALID_PARAMETER);
5020 return FALSE;
5023 /**********************************************************************
5024 * GetMenuInfo (USER32.@)
5026 * NOTES
5027 * win98/NT5.0
5030 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5031 { POPUPMENU *menu;
5033 TRACE("(%p %p)\n", hMenu, lpmi);
5035 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5038 if (lpmi->fMask & MIM_BACKGROUND)
5039 lpmi->hbrBack = menu->hbrBack;
5041 if (lpmi->fMask & MIM_HELPID)
5042 lpmi->dwContextHelpID = menu->dwContextHelpID;
5044 if (lpmi->fMask & MIM_MAXHEIGHT)
5045 lpmi->cyMax = menu->cyMax;
5047 if (lpmi->fMask & MIM_MENUDATA)
5048 lpmi->dwMenuData = menu->dwMenuData;
5050 if (lpmi->fMask & MIM_STYLE)
5051 lpmi->dwStyle = menu->dwStyle;
5053 return TRUE;
5055 SetLastError( ERROR_INVALID_PARAMETER);
5056 return FALSE;
5060 /**********************************************************************
5061 * SetMenuContextHelpId (USER32.@)
5063 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5065 LPPOPUPMENU menu;
5067 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5069 if ((menu = MENU_GetMenu(hMenu)))
5071 menu->dwContextHelpID = dwContextHelpID;
5072 return TRUE;
5074 return FALSE;
5078 /**********************************************************************
5079 * GetMenuContextHelpId (USER32.@)
5081 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5083 LPPOPUPMENU menu;
5085 TRACE("(%p)\n", hMenu);
5087 if ((menu = MENU_GetMenu(hMenu)))
5089 return menu->dwContextHelpID;
5091 return 0;
5094 /**********************************************************************
5095 * MenuItemFromPoint (USER32.@)
5097 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5099 POPUPMENU *menu = MENU_GetMenu(hMenu);
5100 UINT pos;
5102 /*FIXME: Do we have to handle hWnd here? */
5103 if (!menu) return -1;
5104 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5105 return pos;
5109 /**********************************************************************
5110 * translate_accelerator
5112 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5113 BYTE fVirt, WORD key, WORD cmd )
5115 INT mask = 0;
5116 UINT mesg = 0;
5118 if (wParam != key) return FALSE;
5120 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5121 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5122 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5124 if (message == WM_CHAR || message == WM_SYSCHAR)
5126 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5128 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5129 goto found;
5132 else
5134 if(fVirt & FVIRTKEY)
5136 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5137 wParam, 0xff & HIWORD(lParam));
5139 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5140 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5142 else
5144 if (!(lParam & 0x01000000)) /* no special_key */
5146 if ((fVirt & FALT) && (lParam & 0x20000000))
5147 { /* ^^ ALT pressed */
5148 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5149 goto found;
5154 return FALSE;
5156 found:
5157 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5158 mesg = 1;
5159 else
5161 HMENU hMenu, hSubMenu, hSysMenu;
5162 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5164 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5165 hSysMenu = get_win_sys_menu( hWnd );
5167 /* find menu item and ask application to initialize it */
5168 /* 1. in the system menu */
5169 hSubMenu = hSysMenu;
5170 nPos = cmd;
5171 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5173 if (GetCapture())
5174 mesg = 2;
5175 if (!IsWindowEnabled(hWnd))
5176 mesg = 3;
5177 else
5179 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5180 if(hSubMenu != hSysMenu)
5182 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5183 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5184 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5186 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5189 else /* 2. in the window's menu */
5191 hSubMenu = hMenu;
5192 nPos = cmd;
5193 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5195 if (GetCapture())
5196 mesg = 2;
5197 if (!IsWindowEnabled(hWnd))
5198 mesg = 3;
5199 else
5201 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5202 if(hSubMenu != hMenu)
5204 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5205 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5206 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5208 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5213 if (mesg == 0)
5215 if (uSysStat != (UINT)-1)
5217 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5218 mesg=4;
5219 else
5220 mesg=WM_SYSCOMMAND;
5222 else
5224 if (uStat != (UINT)-1)
5226 if (IsIconic(hWnd))
5227 mesg=5;
5228 else
5230 if (uStat & (MF_DISABLED|MF_GRAYED))
5231 mesg=6;
5232 else
5233 mesg=WM_COMMAND;
5236 else
5237 mesg=WM_COMMAND;
5242 if( mesg==WM_COMMAND )
5244 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5245 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5247 else if( mesg==WM_SYSCOMMAND )
5249 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5250 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5252 else
5254 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5255 * #0: unknown (please report!)
5256 * #1: for WM_KEYUP,WM_SYSKEYUP
5257 * #2: mouse is captured
5258 * #3: window is disabled
5259 * #4: it's a disabled system menu option
5260 * #5: it's a menu option, but window is iconic
5261 * #6: it's a menu option, but disabled
5263 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5264 if(mesg==0)
5265 ERR_(accel)(" unknown reason - please report!\n");
5267 return TRUE;
5270 /**********************************************************************
5271 * TranslateAcceleratorA (USER32.@)
5272 * TranslateAccelerator (USER32.@)
5274 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5276 switch (msg->message)
5278 case WM_KEYDOWN:
5279 case WM_SYSKEYDOWN:
5280 return TranslateAcceleratorW( hWnd, hAccel, msg );
5282 case WM_CHAR:
5283 case WM_SYSCHAR:
5285 MSG msgW = *msg;
5286 char ch = LOWORD(msg->wParam);
5287 WCHAR wch;
5288 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5289 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5290 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5293 default:
5294 return 0;
5298 /**********************************************************************
5299 * TranslateAcceleratorW (USER32.@)
5301 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5303 ACCEL data[32], *ptr = data;
5304 int i, count;
5306 if (!hWnd) return 0;
5308 if (msg->message != WM_KEYDOWN &&
5309 msg->message != WM_SYSKEYDOWN &&
5310 msg->message != WM_CHAR &&
5311 msg->message != WM_SYSCHAR)
5312 return 0;
5314 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5315 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5317 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5318 if (count > sizeof(data)/sizeof(data[0]))
5320 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5322 count = CopyAcceleratorTableW( hAccel, ptr, count );
5323 for (i = 0; i < count; i++)
5325 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5326 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5327 break;
5329 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5330 return (i < count);