shlwapi/tests: Fix GLE for SHFormatDateTimeA.
[wine/multimedia.git] / dlls / user32 / menu.c
blob764b7ea3301f96cf339905b12f77e9c257843535
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 /* This makes the menus of applications built with Delphi work.
3274 * It also enables menus to be displayed in more than one window,
3275 * but there are some bugs left that need to be fixed in this case.
3277 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3279 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3280 if (!(wFlags & TPM_NONOTIFY))
3281 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3283 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3285 if (!(wFlags & TPM_NONOTIFY))
3287 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3288 /* If an app changed/recreated menu bar entries in WM_INITMENU
3289 * menu sizes will be recalculated once the menu created/shown.
3293 return TRUE;
3296 /***********************************************************************
3297 * MENU_ExitTracking
3299 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3301 TRACE("hwnd=%p\n", hWnd);
3303 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3304 ShowCaret(0);
3305 top_popup = 0;
3306 top_popup_hmenu = NULL;
3307 return TRUE;
3310 /***********************************************************************
3311 * MENU_TrackMouseMenuBar
3313 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3315 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3317 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3318 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3320 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3322 if (IsMenu(hMenu))
3324 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3325 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3326 MENU_ExitTracking(hWnd, FALSE);
3331 /***********************************************************************
3332 * MENU_TrackKbdMenuBar
3334 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3336 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3338 UINT uItem = NO_SELECTED_ITEM;
3339 HMENU hTrackMenu;
3340 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3342 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3344 /* find window that has a menu */
3346 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3347 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3349 /* check if we have to track a system menu */
3351 hTrackMenu = GetMenu( hwnd );
3352 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3354 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3355 hTrackMenu = get_win_sys_menu( hwnd );
3356 uItem = 0;
3357 wParam |= HTSYSMENU; /* prevent item lookup */
3360 if (!IsMenu( hTrackMenu )) return;
3362 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3364 if( wChar && wChar != ' ' )
3366 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3367 if ( uItem >= (UINT)(-2) )
3369 if( uItem == (UINT)(-1) ) MessageBeep(0);
3370 /* schedule end of menu tracking */
3371 wFlags |= TF_ENDMENU;
3372 goto track_menu;
3376 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3378 if (!(wParam & HTSYSMENU) || wChar == ' ')
3380 if( uItem == NO_SELECTED_ITEM )
3381 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3382 else
3383 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3386 track_menu:
3387 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3388 MENU_ExitTracking( hwnd, FALSE );
3391 /**********************************************************************
3392 * TrackPopupMenuEx (USER32.@)
3394 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3395 HWND hWnd, LPTPMPARAMS lpTpm )
3397 POPUPMENU *menu;
3398 BOOL ret = FALSE;
3400 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3401 hMenu, wFlags, x, y, hWnd, lpTpm,
3402 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3404 /* Parameter check */
3405 /* FIXME: this check is performed several times, here and in the called
3406 functions. That could be optimized */
3407 if (!(menu = MENU_GetMenu( hMenu )))
3409 SetLastError( ERROR_INVALID_MENU_HANDLE );
3410 return FALSE;
3413 if (IsWindow(menu->hWnd))
3415 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3416 return FALSE;
3419 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3421 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3422 if (!(wFlags & TPM_NONOTIFY))
3423 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3425 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3426 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3427 lpTpm ? &lpTpm->rcExclude : NULL );
3428 MENU_ExitTracking(hWnd, TRUE);
3430 return ret;
3433 /**********************************************************************
3434 * TrackPopupMenu (USER32.@)
3436 * Like the win32 API, the function return the command ID only if the
3437 * flag TPM_RETURNCMD is on.
3440 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3441 INT nReserved, HWND hWnd, const RECT *lpRect )
3443 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3446 /***********************************************************************
3447 * PopupMenuWndProc
3449 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3451 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3453 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3455 switch(message)
3457 case WM_CREATE:
3459 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3460 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3461 return 0;
3464 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3465 return MA_NOACTIVATE;
3467 case WM_PAINT:
3469 PAINTSTRUCT ps;
3470 BeginPaint( hwnd, &ps );
3471 MENU_DrawPopupMenu( hwnd, ps.hdc,
3472 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3473 EndPaint( hwnd, &ps );
3474 return 0;
3477 case WM_PRINTCLIENT:
3479 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3480 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3481 return 0;
3484 case WM_ERASEBKGND:
3485 return 1;
3487 case WM_DESTROY:
3488 /* zero out global pointer in case resident popup window was destroyed. */
3489 if (hwnd == top_popup) {
3490 top_popup = 0;
3491 top_popup_hmenu = NULL;
3493 break;
3495 case WM_SHOWWINDOW:
3497 if( wParam )
3499 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3501 else
3502 SetWindowLongPtrW( hwnd, 0, 0 );
3503 break;
3505 case MM_SETMENUHANDLE:
3506 SetWindowLongPtrW( hwnd, 0, wParam );
3507 break;
3509 case MM_GETMENUHANDLE:
3510 case MN_GETHMENU:
3511 return GetWindowLongPtrW( hwnd, 0 );
3513 default:
3514 return DefWindowProcW( hwnd, message, wParam, lParam );
3516 return 0;
3520 /***********************************************************************
3521 * MENU_GetMenuBarHeight
3523 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3525 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3526 INT orgX, INT orgY )
3528 HDC hdc;
3529 RECT rectBar;
3530 LPPOPUPMENU lppop;
3532 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3534 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3536 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3537 SelectObject( hdc, get_menu_font(FALSE));
3538 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3539 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3540 ReleaseDC( hwnd, hdc );
3541 return lppop->Height;
3545 /*******************************************************************
3546 * ChangeMenuA (USER32.@)
3548 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3549 UINT id, UINT flags )
3551 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3552 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3553 id, data );
3554 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3555 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3556 id, data );
3557 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3558 flags & MF_BYPOSITION ? pos : id,
3559 flags & ~MF_REMOVE );
3560 /* Default: MF_INSERT */
3561 return InsertMenuA( hMenu, pos, flags, id, data );
3565 /*******************************************************************
3566 * ChangeMenuW (USER32.@)
3568 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3569 UINT id, UINT flags )
3571 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3572 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3573 id, data );
3574 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3575 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3576 id, data );
3577 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3578 flags & MF_BYPOSITION ? pos : id,
3579 flags & ~MF_REMOVE );
3580 /* Default: MF_INSERT */
3581 return InsertMenuW( hMenu, pos, flags, id, data );
3585 /*******************************************************************
3586 * CheckMenuItem (USER32.@)
3588 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3590 MENUITEM *item;
3591 DWORD ret;
3593 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3594 ret = item->fState & MF_CHECKED;
3595 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3596 else item->fState &= ~MF_CHECKED;
3597 return ret;
3601 /**********************************************************************
3602 * EnableMenuItem (USER32.@)
3604 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3606 UINT oldflags;
3607 MENUITEM *item;
3608 POPUPMENU *menu;
3610 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3612 /* Get the Popupmenu to access the owner menu */
3613 if (!(menu = MENU_GetMenu(hMenu)))
3614 return (UINT)-1;
3616 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3617 return (UINT)-1;
3619 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3620 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3622 /* If the close item in the system menu change update the close button */
3623 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3625 if (menu->hSysMenuOwner != 0)
3627 RECT rc;
3628 POPUPMENU* parentMenu;
3630 /* Get the parent menu to access*/
3631 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3632 return (UINT)-1;
3634 /* Refresh the frame to reflect the change */
3635 GetWindowRect(parentMenu->hWnd, &rc);
3636 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3637 rc.bottom = 0;
3638 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3642 return oldflags;
3646 /*******************************************************************
3647 * GetMenuStringA (USER32.@)
3649 INT WINAPI GetMenuStringA(
3650 HMENU hMenu, /* [in] menuhandle */
3651 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3652 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3653 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3654 UINT wFlags /* [in] MF_ flags */
3656 MENUITEM *item;
3658 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3659 if (str && nMaxSiz) str[0] = '\0';
3660 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3661 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3662 return 0;
3664 if (!item->text) return 0;
3665 if (!str || !nMaxSiz) return strlenW(item->text);
3666 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3667 str[nMaxSiz-1] = 0;
3668 TRACE("returning %s\n", debugstr_a(str));
3669 return strlen(str);
3673 /*******************************************************************
3674 * GetMenuStringW (USER32.@)
3676 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3677 LPWSTR str, INT nMaxSiz, UINT wFlags )
3679 MENUITEM *item;
3681 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3682 if (str && nMaxSiz) str[0] = '\0';
3683 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3684 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3685 return 0;
3687 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3688 if( !(item->text)) {
3689 str[0] = 0;
3690 return 0;
3692 lstrcpynW( str, item->text, nMaxSiz );
3693 TRACE("returning %s\n", debugstr_w(str));
3694 return strlenW(str);
3698 /**********************************************************************
3699 * HiliteMenuItem (USER32.@)
3701 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3702 UINT wHilite )
3704 LPPOPUPMENU menu;
3705 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3706 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3707 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3708 if (menu->FocusedItem == wItemID) return TRUE;
3709 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3710 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3711 return TRUE;
3715 /**********************************************************************
3716 * GetMenuState (USER32.@)
3718 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3720 MENUITEM *item;
3721 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3722 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3723 debug_print_menuitem (" item: ", item, "");
3724 if (item->fType & MF_POPUP)
3726 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3727 if (!menu) return -1;
3728 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3730 else
3732 /* We used to (from way back then) mask the result to 0xff. */
3733 /* I don't know why and it seems wrong as the documented */
3734 /* return flag MF_SEPARATOR is outside that mask. */
3735 return (item->fType | item->fState);
3740 /**********************************************************************
3741 * GetMenuItemCount (USER32.@)
3743 INT WINAPI GetMenuItemCount( HMENU hMenu )
3745 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3746 if (!menu) return -1;
3747 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3748 return menu->nItems;
3752 /**********************************************************************
3753 * GetMenuItemID (USER32.@)
3755 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3757 MENUITEM * lpmi;
3759 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3760 if (lpmi->fType & MF_POPUP) return -1;
3761 return lpmi->wID;
3766 /**********************************************************************
3767 * MENU_mnu2mnuii
3769 * Uses flags, id and text ptr, passed by InsertMenu() and
3770 * ModifyMenu() to setup a MenuItemInfo structure.
3772 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3773 LPMENUITEMINFOW pmii)
3775 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3776 pmii->cbSize = sizeof( MENUITEMINFOW);
3777 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3778 /* setting bitmap clears text and vice versa */
3779 if( IS_STRING_ITEM(flags)) {
3780 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3781 if( !str)
3782 flags |= MF_SEPARATOR;
3783 /* Item beginning with a backspace is a help item */
3784 /* FIXME: wrong place, this is only true in win16 */
3785 else if( *str == '\b') {
3786 flags |= MF_HELP;
3787 str++;
3789 pmii->dwTypeData = (LPWSTR)str;
3790 } else if( flags & MFT_BITMAP){
3791 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3792 pmii->hbmpItem = ULongToHandle(LOWORD(str));
3794 if( flags & MF_OWNERDRAW){
3795 pmii->fMask |= MIIM_DATA;
3796 pmii->dwItemData = (ULONG_PTR) str;
3798 if( flags & MF_POPUP) {
3799 pmii->fMask |= MIIM_SUBMENU;
3800 pmii->hSubMenu = (HMENU)id;
3802 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3803 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3804 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3805 pmii->wID = (UINT)id;
3809 /*******************************************************************
3810 * InsertMenuW (USER32.@)
3812 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3813 UINT_PTR id, LPCWSTR str )
3815 MENUITEM *item;
3816 MENUITEMINFOW mii;
3818 if (IS_STRING_ITEM(flags) && str)
3819 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3820 hMenu, pos, flags, id, debugstr_w(str) );
3821 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3822 hMenu, pos, flags, id, str );
3824 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3825 MENU_mnu2mnuii( flags, id, str, &mii);
3826 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3828 RemoveMenu( hMenu, pos, flags );
3829 return FALSE;
3832 item->hCheckBit = item->hUnCheckBit = 0;
3833 return TRUE;
3837 /*******************************************************************
3838 * InsertMenuA (USER32.@)
3840 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3841 UINT_PTR id, LPCSTR str )
3843 BOOL ret = FALSE;
3845 if (IS_STRING_ITEM(flags) && str)
3847 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3848 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3849 if (newstr)
3851 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3852 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3853 HeapFree( GetProcessHeap(), 0, newstr );
3855 return ret;
3857 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3861 /*******************************************************************
3862 * AppendMenuA (USER32.@)
3864 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3865 UINT_PTR id, LPCSTR data )
3867 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3871 /*******************************************************************
3872 * AppendMenuW (USER32.@)
3874 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3875 UINT_PTR id, LPCWSTR data )
3877 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3881 /**********************************************************************
3882 * RemoveMenu (USER32.@)
3884 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3886 LPPOPUPMENU menu;
3887 MENUITEM *item;
3889 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3890 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3891 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3893 /* Remove item */
3895 MENU_FreeItemData( item );
3897 if (--menu->nItems == 0)
3899 HeapFree( GetProcessHeap(), 0, menu->items );
3900 menu->items = NULL;
3902 else
3904 while(nPos < menu->nItems)
3906 *item = *(item+1);
3907 item++;
3908 nPos++;
3910 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3911 menu->nItems * sizeof(MENUITEM) );
3913 return TRUE;
3917 /**********************************************************************
3918 * DeleteMenu (USER32.@)
3920 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3922 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3923 if (!item) return FALSE;
3924 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3925 /* nPos is now the position of the item */
3926 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3927 return TRUE;
3931 /*******************************************************************
3932 * ModifyMenuW (USER32.@)
3934 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3935 UINT_PTR id, LPCWSTR str )
3937 MENUITEM *item;
3938 MENUITEMINFOW mii;
3940 if (IS_STRING_ITEM(flags))
3941 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3942 else
3943 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3945 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3946 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3947 MENU_mnu2mnuii( flags, id, str, &mii);
3948 return SetMenuItemInfo_common( item, &mii, TRUE);
3952 /*******************************************************************
3953 * ModifyMenuA (USER32.@)
3955 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3956 UINT_PTR id, LPCSTR str )
3958 BOOL ret = FALSE;
3960 if (IS_STRING_ITEM(flags) && str)
3962 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3963 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3964 if (newstr)
3966 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3967 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3968 HeapFree( GetProcessHeap(), 0, newstr );
3970 return ret;
3972 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3976 /**********************************************************************
3977 * CreatePopupMenu (USER32.@)
3979 HMENU WINAPI CreatePopupMenu(void)
3981 HMENU hmenu;
3982 POPUPMENU *menu;
3984 if (!(hmenu = CreateMenu())) return 0;
3985 menu = MENU_GetMenu( hmenu );
3986 menu->wFlags |= MF_POPUP;
3987 menu->bTimeToHide = FALSE;
3988 return hmenu;
3992 /**********************************************************************
3993 * GetMenuCheckMarkDimensions (USER.417)
3994 * GetMenuCheckMarkDimensions (USER32.@)
3996 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3998 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4002 /**********************************************************************
4003 * SetMenuItemBitmaps (USER32.@)
4005 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4006 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4008 MENUITEM *item;
4010 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4012 if (!hNewCheck && !hNewUnCheck)
4014 item->fState &= ~MF_USECHECKBITMAPS;
4016 else /* Install new bitmaps */
4018 item->hCheckBit = hNewCheck;
4019 item->hUnCheckBit = hNewUnCheck;
4020 item->fState |= MF_USECHECKBITMAPS;
4022 return TRUE;
4026 /**********************************************************************
4027 * CreateMenu (USER32.@)
4029 HMENU WINAPI CreateMenu(void)
4031 HMENU hMenu;
4032 LPPOPUPMENU menu;
4034 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4035 menu->FocusedItem = NO_SELECTED_ITEM;
4036 menu->bTimeToHide = FALSE;
4038 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4040 TRACE("return %p\n", hMenu );
4042 return hMenu;
4046 /**********************************************************************
4047 * DestroyMenu (USER32.@)
4049 BOOL WINAPI DestroyMenu( HMENU hMenu )
4051 LPPOPUPMENU lppop;
4053 TRACE("(%p)\n", hMenu);
4055 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4056 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4058 /* DestroyMenu should not destroy system menu popup owner */
4059 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4061 DestroyWindow( lppop->hWnd );
4062 lppop->hWnd = 0;
4065 if (lppop->items) /* recursively destroy submenus */
4067 int i;
4068 MENUITEM *item = lppop->items;
4069 for (i = lppop->nItems; i > 0; i--, item++)
4071 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4072 MENU_FreeItemData( item );
4074 HeapFree( GetProcessHeap(), 0, lppop->items );
4076 HeapFree( GetProcessHeap(), 0, lppop );
4077 return TRUE;
4081 /**********************************************************************
4082 * GetSystemMenu (USER32.@)
4084 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4086 WND *wndPtr = WIN_GetPtr( hWnd );
4087 HMENU retvalue = 0;
4089 if (wndPtr == WND_DESKTOP) return 0;
4090 if (wndPtr == WND_OTHER_PROCESS)
4092 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4094 else if (wndPtr)
4096 if (wndPtr->hSysMenu && bRevert)
4098 DestroyMenu(wndPtr->hSysMenu);
4099 wndPtr->hSysMenu = 0;
4102 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4103 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4105 if( wndPtr->hSysMenu )
4107 POPUPMENU *menu;
4108 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4110 /* Store the dummy sysmenu handle to facilitate the refresh */
4111 /* of the close button if the SC_CLOSE item change */
4112 menu = MENU_GetMenu(retvalue);
4113 if ( menu )
4114 menu->hSysMenuOwner = wndPtr->hSysMenu;
4116 WIN_ReleasePtr( wndPtr );
4118 return bRevert ? 0 : retvalue;
4122 /*******************************************************************
4123 * SetSystemMenu (USER32.@)
4125 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4127 WND *wndPtr = WIN_GetPtr( hwnd );
4129 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4131 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4132 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4133 WIN_ReleasePtr( wndPtr );
4134 return TRUE;
4136 return FALSE;
4140 /**********************************************************************
4141 * GetMenu (USER32.@)
4143 HMENU WINAPI GetMenu( HWND hWnd )
4145 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4146 TRACE("for %p returning %p\n", hWnd, retvalue);
4147 return retvalue;
4150 /**********************************************************************
4151 * GetMenuBarInfo (USER32.@)
4153 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4155 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4156 return FALSE;
4159 /**********************************************************************
4160 * MENU_SetMenu
4162 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4163 * SetWindowPos call that would result if SetMenu were called directly.
4165 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4167 TRACE("(%p, %p);\n", hWnd, hMenu);
4169 if (hMenu && !IsMenu(hMenu))
4171 WARN("hMenu %p is not a menu handle\n", hMenu);
4172 return FALSE;
4174 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4175 return FALSE;
4177 hWnd = WIN_GetFullHandle( hWnd );
4178 if (GetCapture() == hWnd)
4179 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4181 if (hMenu != 0)
4183 LPPOPUPMENU lpmenu;
4185 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4187 lpmenu->hWnd = hWnd;
4188 lpmenu->Height = 0; /* Make sure we recalculate the size */
4190 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4191 return TRUE;
4195 /**********************************************************************
4196 * SetMenu (USER32.@)
4198 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4200 if(!MENU_SetMenu(hWnd, hMenu))
4201 return FALSE;
4203 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4204 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4205 return TRUE;
4209 /**********************************************************************
4210 * GetSubMenu (USER32.@)
4212 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4214 MENUITEM * lpmi;
4216 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4217 if (!(lpmi->fType & MF_POPUP)) return 0;
4218 return lpmi->hSubMenu;
4222 /**********************************************************************
4223 * DrawMenuBar (USER32.@)
4225 BOOL WINAPI DrawMenuBar( HWND hWnd )
4227 LPPOPUPMENU lppop;
4228 HMENU hMenu = GetMenu(hWnd);
4230 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4231 return FALSE;
4232 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4234 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4235 lppop->hwndOwner = hWnd;
4236 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4237 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4238 return TRUE;
4241 /***********************************************************************
4242 * DrawMenuBarTemp (USER32.@)
4244 * UNDOCUMENTED !!
4246 * called by W98SE desk.cpl Control Panel Applet
4248 * Not 100% sure about the param names, but close.
4250 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4252 LPPOPUPMENU lppop;
4253 UINT i,retvalue;
4254 HFONT hfontOld = 0;
4255 BOOL flat_menu = FALSE;
4257 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4259 if (!hMenu)
4260 hMenu = GetMenu(hwnd);
4262 if (!hFont)
4263 hFont = get_menu_font(FALSE);
4265 lppop = MENU_GetMenu( hMenu );
4266 if (lppop == NULL || lprect == NULL)
4268 retvalue = GetSystemMetrics(SM_CYMENU);
4269 goto END;
4272 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4274 hfontOld = SelectObject( hDC, hFont);
4276 if (lppop->Height == 0)
4277 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4279 lprect->bottom = lprect->top + lppop->Height;
4281 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4283 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4284 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4285 LineTo( hDC, lprect->right, lprect->bottom );
4287 if (lppop->nItems == 0)
4289 retvalue = GetSystemMetrics(SM_CYMENU);
4290 goto END;
4293 for (i = 0; i < lppop->nItems; i++)
4295 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4296 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4298 retvalue = lppop->Height;
4300 END:
4301 if (hfontOld) SelectObject (hDC, hfontOld);
4302 return retvalue;
4305 /***********************************************************************
4306 * EndMenu (USER.187)
4307 * EndMenu (USER32.@)
4309 BOOL WINAPI EndMenu(void)
4311 /* if we are in the menu code, and it is active */
4312 if (!fEndMenu && top_popup)
4314 /* terminate the menu handling code */
4315 fEndMenu = TRUE;
4317 /* needs to be posted to wakeup the internal menu handler */
4318 /* which will now terminate the menu, in the event that */
4319 /* the main window was minimized, or lost focus, so we */
4320 /* don't end up with an orphaned menu */
4321 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4323 return fEndMenu;
4327 /*****************************************************************
4328 * LoadMenuA (USER32.@)
4330 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4332 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4333 if (!hrsrc) return 0;
4334 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4338 /*****************************************************************
4339 * LoadMenuW (USER32.@)
4341 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4343 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4344 if (!hrsrc) return 0;
4345 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4349 /**********************************************************************
4350 * LoadMenuIndirectW (USER32.@)
4352 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4354 HMENU hMenu;
4355 WORD version, offset;
4356 LPCSTR p = template;
4358 version = GET_WORD(p);
4359 p += sizeof(WORD);
4360 TRACE("%p, ver %d\n", template, version );
4361 switch (version)
4363 case 0: /* standard format is version of 0 */
4364 offset = GET_WORD(p);
4365 p += sizeof(WORD) + offset;
4366 if (!(hMenu = CreateMenu())) return 0;
4367 if (!MENU_ParseResource( p, hMenu ))
4369 DestroyMenu( hMenu );
4370 return 0;
4372 return hMenu;
4373 case 1: /* extended format is version of 1 */
4374 offset = GET_WORD(p);
4375 p += sizeof(WORD) + offset;
4376 if (!(hMenu = CreateMenu())) return 0;
4377 if (!MENUEX_ParseResource( p, hMenu))
4379 DestroyMenu( hMenu );
4380 return 0;
4382 return hMenu;
4383 default:
4384 ERR("version %d not supported.\n", version);
4385 return 0;
4390 /**********************************************************************
4391 * LoadMenuIndirectA (USER32.@)
4393 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4395 return LoadMenuIndirectW( template );
4399 /**********************************************************************
4400 * IsMenu (USER32.@)
4402 BOOL WINAPI IsMenu(HMENU hmenu)
4404 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4406 if (!menu)
4408 SetLastError(ERROR_INVALID_MENU_HANDLE);
4409 return FALSE;
4411 return TRUE;
4414 /**********************************************************************
4415 * GetMenuItemInfo_common
4418 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4419 LPMENUITEMINFOW lpmii, BOOL unicode)
4421 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4423 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4425 if (!menu) {
4426 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4427 return FALSE;
4430 if( lpmii->fMask & MIIM_TYPE) {
4431 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4432 WARN("invalid combination of fMask bits used\n");
4433 /* this does not happen on Win9x/ME */
4434 SetLastError( ERROR_INVALID_PARAMETER);
4435 return FALSE;
4437 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4438 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4439 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4440 if( lpmii->fType & MFT_BITMAP) {
4441 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4442 lpmii->cch = 0;
4443 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4444 /* this does not happen on Win9x/ME */
4445 lpmii->dwTypeData = 0;
4446 lpmii->cch = 0;
4450 /* copy the text string */
4451 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4452 if( !menu->text ) {
4453 if(lpmii->dwTypeData && lpmii->cch) {
4454 lpmii->cch = 0;
4455 if( unicode)
4456 *((WCHAR *)lpmii->dwTypeData) = 0;
4457 else
4458 *((CHAR *)lpmii->dwTypeData) = 0;
4460 } else {
4461 int len;
4462 if (unicode)
4464 len = strlenW(menu->text);
4465 if(lpmii->dwTypeData && lpmii->cch)
4466 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4468 else
4470 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4471 0, NULL, NULL ) - 1;
4472 if(lpmii->dwTypeData && lpmii->cch)
4473 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4474 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4475 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4477 /* if we've copied a substring we return its length */
4478 if(lpmii->dwTypeData && lpmii->cch)
4479 if (lpmii->cch <= len + 1)
4480 lpmii->cch--;
4481 else
4482 lpmii->cch = len;
4483 else {
4484 /* return length of string */
4485 /* not on Win9x/ME if fType & MFT_BITMAP */
4486 lpmii->cch = len;
4491 if (lpmii->fMask & MIIM_FTYPE)
4492 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4494 if (lpmii->fMask & MIIM_BITMAP)
4495 lpmii->hbmpItem = menu->hbmpItem;
4497 if (lpmii->fMask & MIIM_STATE)
4498 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4500 if (lpmii->fMask & MIIM_ID)
4501 lpmii->wID = menu->wID;
4503 if (lpmii->fMask & MIIM_SUBMENU)
4504 lpmii->hSubMenu = menu->hSubMenu;
4505 else {
4506 /* hSubMenu is always cleared
4507 * (not on Win9x/ME ) */
4508 lpmii->hSubMenu = 0;
4511 if (lpmii->fMask & MIIM_CHECKMARKS) {
4512 lpmii->hbmpChecked = menu->hCheckBit;
4513 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4515 if (lpmii->fMask & MIIM_DATA)
4516 lpmii->dwItemData = menu->dwItemData;
4518 return TRUE;
4521 /**********************************************************************
4522 * GetMenuItemInfoA (USER32.@)
4524 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4525 LPMENUITEMINFOA lpmii)
4527 BOOL ret;
4528 MENUITEMINFOA mii;
4529 if( lpmii->cbSize != sizeof( mii) &&
4530 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4531 SetLastError( ERROR_INVALID_PARAMETER);
4532 return FALSE;
4534 memcpy( &mii, lpmii, lpmii->cbSize);
4535 mii.cbSize = sizeof( mii);
4536 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4537 (LPMENUITEMINFOW)&mii, FALSE);
4538 mii.cbSize = lpmii->cbSize;
4539 memcpy( lpmii, &mii, mii.cbSize);
4540 return ret;
4543 /**********************************************************************
4544 * GetMenuItemInfoW (USER32.@)
4546 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4547 LPMENUITEMINFOW lpmii)
4549 BOOL ret;
4550 MENUITEMINFOW mii;
4551 if( lpmii->cbSize != sizeof( mii) &&
4552 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4553 SetLastError( ERROR_INVALID_PARAMETER);
4554 return FALSE;
4556 memcpy( &mii, lpmii, lpmii->cbSize);
4557 mii.cbSize = sizeof( mii);
4558 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4559 mii.cbSize = lpmii->cbSize;
4560 memcpy( lpmii, &mii, mii.cbSize);
4561 return ret;
4565 /* set a menu item text from a ASCII or Unicode string */
4566 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4568 if (!text)
4569 menu->text = NULL;
4570 else if (unicode)
4572 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4573 strcpyW( menu->text, text );
4575 else
4577 LPCSTR str = (LPCSTR)text;
4578 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4579 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4580 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4585 /**********************************************************************
4586 * MENU_depth
4588 * detect if there are loops in the menu tree (or the depth is too large)
4590 static int MENU_depth( POPUPMENU *pmenu, int depth)
4592 int i;
4593 MENUITEM *item;
4594 int subdepth;
4596 depth++;
4597 if( depth > MAXMENUDEPTH) return depth;
4598 item = pmenu->items;
4599 subdepth = depth;
4600 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4601 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4602 if( psubmenu){
4603 int bdepth = MENU_depth( psubmenu, depth);
4604 if( bdepth > subdepth) subdepth = bdepth;
4606 if( subdepth > MAXMENUDEPTH)
4607 TRACE("<- hmenu %p\n", item->hSubMenu);
4609 return subdepth;
4613 /**********************************************************************
4614 * SetMenuItemInfo_common
4616 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4617 * MIIM_BITMAP and MIIM_STRING flags instead.
4620 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4621 const MENUITEMINFOW *lpmii,
4622 BOOL unicode)
4624 if (!menu) return FALSE;
4626 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4628 if (lpmii->fMask & MIIM_FTYPE ) {
4629 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4630 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4632 if (lpmii->fMask & MIIM_STRING ) {
4633 /* free the string when used */
4634 HeapFree(GetProcessHeap(), 0, menu->text);
4635 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4638 if (lpmii->fMask & MIIM_STATE)
4639 /* Other menu items having MFS_DEFAULT are not converted
4640 to normal items */
4641 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4643 if (lpmii->fMask & MIIM_ID)
4644 menu->wID = lpmii->wID;
4646 if (lpmii->fMask & MIIM_SUBMENU) {
4647 menu->hSubMenu = lpmii->hSubMenu;
4648 if (menu->hSubMenu) {
4649 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4650 if (subMenu) {
4651 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4652 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4653 menu->hSubMenu = 0;
4654 return FALSE;
4656 subMenu->wFlags |= MF_POPUP;
4657 menu->fType |= MF_POPUP;
4658 } else {
4659 SetLastError( ERROR_INVALID_PARAMETER);
4660 return FALSE;
4663 else
4664 menu->fType &= ~MF_POPUP;
4667 if (lpmii->fMask & MIIM_CHECKMARKS)
4669 menu->hCheckBit = lpmii->hbmpChecked;
4670 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4672 if (lpmii->fMask & MIIM_DATA)
4673 menu->dwItemData = lpmii->dwItemData;
4675 if (lpmii->fMask & MIIM_BITMAP)
4676 menu->hbmpItem = lpmii->hbmpItem;
4678 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4679 menu->fType |= MFT_SEPARATOR;
4681 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4682 return TRUE;
4685 /**********************************************************************
4686 * MENU_NormalizeMenuItemInfoStruct
4688 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4689 * check, copy and extend the MENUITEMINFO struct from the version that the application
4690 * supplied to the version used by wine source. */
4691 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4692 MENUITEMINFOW *pmii_out )
4694 /* do we recognize the size? */
4695 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4696 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4697 SetLastError( ERROR_INVALID_PARAMETER);
4698 return FALSE;
4700 /* copy the fields that we have */
4701 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4702 /* if the hbmpItem member is missing then extend */
4703 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4704 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4705 pmii_out->hbmpItem = NULL;
4707 /* test for invalid bit combinations */
4708 if( (pmii_out->fMask & MIIM_TYPE &&
4709 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4710 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4711 WARN("invalid combination of fMask bits used\n");
4712 /* this does not happen on Win9x/ME */
4713 SetLastError( ERROR_INVALID_PARAMETER);
4714 return FALSE;
4716 /* convert old style (MIIM_TYPE) to the new */
4717 if( pmii_out->fMask & MIIM_TYPE){
4718 pmii_out->fMask |= MIIM_FTYPE;
4719 if( IS_STRING_ITEM(pmii_out->fType)){
4720 pmii_out->fMask |= MIIM_STRING;
4721 } else if( (pmii_out->fType) & MFT_BITMAP){
4722 pmii_out->fMask |= MIIM_BITMAP;
4723 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4726 return TRUE;
4729 /**********************************************************************
4730 * SetMenuItemInfoA (USER32.@)
4732 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4733 const MENUITEMINFOA *lpmii)
4735 MENUITEMINFOW mii;
4737 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4739 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4741 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4742 &mii, FALSE);
4745 /**********************************************************************
4746 * SetMenuItemInfoW (USER32.@)
4748 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4749 const MENUITEMINFOW *lpmii)
4751 MENUITEMINFOW mii;
4753 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4755 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4756 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4757 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4760 /**********************************************************************
4761 * SetMenuDefaultItem (USER32.@)
4764 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4766 UINT i;
4767 POPUPMENU *menu;
4768 MENUITEM *item;
4770 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4772 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4774 /* reset all default-item flags */
4775 item = menu->items;
4776 for (i = 0; i < menu->nItems; i++, item++)
4778 item->fState &= ~MFS_DEFAULT;
4781 /* no default item */
4782 if ( -1 == uItem)
4784 return TRUE;
4787 item = menu->items;
4788 if ( bypos )
4790 if ( uItem >= menu->nItems ) return FALSE;
4791 item[uItem].fState |= MFS_DEFAULT;
4792 return TRUE;
4794 else
4796 for (i = 0; i < menu->nItems; i++, item++)
4798 if (item->wID == uItem)
4800 item->fState |= MFS_DEFAULT;
4801 return TRUE;
4806 return FALSE;
4809 /**********************************************************************
4810 * GetMenuDefaultItem (USER32.@)
4812 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4814 POPUPMENU *menu;
4815 MENUITEM * item;
4816 UINT i = 0;
4818 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4820 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4822 /* find default item */
4823 item = menu->items;
4825 /* empty menu */
4826 if (! item) return -1;
4828 while ( !( item->fState & MFS_DEFAULT ) )
4830 i++; item++;
4831 if (i >= menu->nItems ) return -1;
4834 /* default: don't return disabled items */
4835 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4837 /* search rekursiv when needed */
4838 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4840 UINT ret;
4841 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4842 if ( -1 != ret ) return ret;
4844 /* when item not found in submenu, return the popup item */
4846 return ( bypos ) ? i : item->wID;
4851 /**********************************************************************
4852 * InsertMenuItemA (USER32.@)
4854 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4855 const MENUITEMINFOA *lpmii)
4857 MENUITEM *item;
4858 MENUITEMINFOW mii;
4860 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4862 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4864 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4865 return SetMenuItemInfo_common(item, &mii, FALSE);
4869 /**********************************************************************
4870 * InsertMenuItemW (USER32.@)
4872 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4873 const MENUITEMINFOW *lpmii)
4875 MENUITEM *item;
4876 MENUITEMINFOW mii;
4878 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4880 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4882 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4883 return SetMenuItemInfo_common(item, &mii, TRUE);
4886 /**********************************************************************
4887 * CheckMenuRadioItem (USER32.@)
4890 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4891 UINT first, UINT last, UINT check,
4892 UINT bypos)
4894 BOOL done = FALSE;
4895 UINT i;
4896 MENUITEM *mi_first = NULL, *mi_check;
4897 HMENU m_first, m_check;
4899 for (i = first; i <= last; i++)
4901 UINT pos = i;
4903 if (!mi_first)
4905 m_first = hMenu;
4906 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4907 if (!mi_first) continue;
4908 mi_check = mi_first;
4909 m_check = m_first;
4911 else
4913 m_check = hMenu;
4914 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4915 if (!mi_check) continue;
4918 if (m_first != m_check) continue;
4919 if (mi_check->fType == MFT_SEPARATOR) continue;
4921 if (i == check)
4923 mi_check->fType |= MFT_RADIOCHECK;
4924 mi_check->fState |= MFS_CHECKED;
4925 done = TRUE;
4927 else
4929 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4930 mi_check->fState &= ~MFS_CHECKED;
4934 return done;
4938 /**********************************************************************
4939 * GetMenuItemRect (USER32.@)
4941 * ATTENTION: Here, the returned values in rect are the screen
4942 * coordinates of the item just like if the menu was
4943 * always on the upper left side of the application.
4946 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4947 LPRECT rect)
4949 POPUPMENU *itemMenu;
4950 MENUITEM *item;
4951 HWND referenceHwnd;
4953 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4955 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4956 referenceHwnd = hwnd;
4958 if(!hwnd)
4960 itemMenu = MENU_GetMenu(hMenu);
4961 if (itemMenu == NULL)
4962 return FALSE;
4964 if(itemMenu->hWnd == 0)
4965 return FALSE;
4966 referenceHwnd = itemMenu->hWnd;
4969 if ((rect == NULL) || (item == NULL))
4970 return FALSE;
4972 *rect = item->rect;
4974 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4976 return TRUE;
4979 /**********************************************************************
4980 * SetMenuInfo (USER32.@)
4982 * FIXME
4983 * actually use the items to draw the menu
4984 * (recalculate and/or redraw)
4986 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
4988 POPUPMENU *menu;
4989 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
4991 if (lpmi->fMask & MIM_BACKGROUND)
4992 menu->hbrBack = lpmi->hbrBack;
4994 if (lpmi->fMask & MIM_HELPID)
4995 menu->dwContextHelpID = lpmi->dwContextHelpID;
4997 if (lpmi->fMask & MIM_MAXHEIGHT)
4998 menu->cyMax = lpmi->cyMax;
5000 if (lpmi->fMask & MIM_MENUDATA)
5001 menu->dwMenuData = lpmi->dwMenuData;
5003 if (lpmi->fMask & MIM_STYLE)
5004 menu->dwStyle = lpmi->dwStyle;
5006 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5007 int i;
5008 MENUITEM *item = menu->items;
5009 for( i = menu->nItems; i; i--, item++)
5010 if( item->fType & MF_POPUP)
5011 menu_SetMenuInfo( item->hSubMenu, lpmi);
5013 return TRUE;
5016 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5018 TRACE("(%p %p)\n", hMenu, lpmi);
5019 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5020 if( lpmi->fMask & MIM_STYLE) {
5021 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5022 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5023 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5025 return TRUE;
5027 SetLastError( ERROR_INVALID_PARAMETER);
5028 return FALSE;
5031 /**********************************************************************
5032 * GetMenuInfo (USER32.@)
5034 * NOTES
5035 * win98/NT5.0
5038 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5039 { POPUPMENU *menu;
5041 TRACE("(%p %p)\n", hMenu, lpmi);
5043 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5046 if (lpmi->fMask & MIM_BACKGROUND)
5047 lpmi->hbrBack = menu->hbrBack;
5049 if (lpmi->fMask & MIM_HELPID)
5050 lpmi->dwContextHelpID = menu->dwContextHelpID;
5052 if (lpmi->fMask & MIM_MAXHEIGHT)
5053 lpmi->cyMax = menu->cyMax;
5055 if (lpmi->fMask & MIM_MENUDATA)
5056 lpmi->dwMenuData = menu->dwMenuData;
5058 if (lpmi->fMask & MIM_STYLE)
5059 lpmi->dwStyle = menu->dwStyle;
5061 return TRUE;
5063 SetLastError( ERROR_INVALID_PARAMETER);
5064 return FALSE;
5068 /**********************************************************************
5069 * SetMenuContextHelpId (USER32.@)
5071 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5073 LPPOPUPMENU menu;
5075 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5077 if ((menu = MENU_GetMenu(hMenu)))
5079 menu->dwContextHelpID = dwContextHelpID;
5080 return TRUE;
5082 return FALSE;
5086 /**********************************************************************
5087 * GetMenuContextHelpId (USER32.@)
5089 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5091 LPPOPUPMENU menu;
5093 TRACE("(%p)\n", hMenu);
5095 if ((menu = MENU_GetMenu(hMenu)))
5097 return menu->dwContextHelpID;
5099 return 0;
5102 /**********************************************************************
5103 * MenuItemFromPoint (USER32.@)
5105 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5107 POPUPMENU *menu = MENU_GetMenu(hMenu);
5108 UINT pos;
5110 /*FIXME: Do we have to handle hWnd here? */
5111 if (!menu) return -1;
5112 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5113 return pos;
5117 /**********************************************************************
5118 * translate_accelerator
5120 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5121 BYTE fVirt, WORD key, WORD cmd )
5123 INT mask = 0;
5124 UINT mesg = 0;
5126 if (wParam != key) return FALSE;
5128 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5129 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5130 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5132 if (message == WM_CHAR || message == WM_SYSCHAR)
5134 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5136 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5137 goto found;
5140 else
5142 if(fVirt & FVIRTKEY)
5144 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5145 wParam, 0xff & HIWORD(lParam));
5147 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5148 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5150 else
5152 if (!(lParam & 0x01000000)) /* no special_key */
5154 if ((fVirt & FALT) && (lParam & 0x20000000))
5155 { /* ^^ ALT pressed */
5156 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5157 goto found;
5162 return FALSE;
5164 found:
5165 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5166 mesg = 1;
5167 else
5169 HMENU hMenu, hSubMenu, hSysMenu;
5170 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5172 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5173 hSysMenu = get_win_sys_menu( hWnd );
5175 /* find menu item and ask application to initialize it */
5176 /* 1. in the system menu */
5177 hSubMenu = hSysMenu;
5178 nPos = cmd;
5179 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5181 if (GetCapture())
5182 mesg = 2;
5183 if (!IsWindowEnabled(hWnd))
5184 mesg = 3;
5185 else
5187 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5188 if(hSubMenu != hSysMenu)
5190 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5191 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5192 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5194 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5197 else /* 2. in the window's menu */
5199 hSubMenu = hMenu;
5200 nPos = cmd;
5201 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5203 if (GetCapture())
5204 mesg = 2;
5205 if (!IsWindowEnabled(hWnd))
5206 mesg = 3;
5207 else
5209 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5210 if(hSubMenu != hMenu)
5212 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5213 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5214 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5216 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5221 if (mesg == 0)
5223 if (uSysStat != (UINT)-1)
5225 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5226 mesg=4;
5227 else
5228 mesg=WM_SYSCOMMAND;
5230 else
5232 if (uStat != (UINT)-1)
5234 if (IsIconic(hWnd))
5235 mesg=5;
5236 else
5238 if (uStat & (MF_DISABLED|MF_GRAYED))
5239 mesg=6;
5240 else
5241 mesg=WM_COMMAND;
5244 else
5245 mesg=WM_COMMAND;
5250 if( mesg==WM_COMMAND )
5252 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5253 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5255 else if( mesg==WM_SYSCOMMAND )
5257 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5258 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5260 else
5262 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5263 * #0: unknown (please report!)
5264 * #1: for WM_KEYUP,WM_SYSKEYUP
5265 * #2: mouse is captured
5266 * #3: window is disabled
5267 * #4: it's a disabled system menu option
5268 * #5: it's a menu option, but window is iconic
5269 * #6: it's a menu option, but disabled
5271 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5272 if(mesg==0)
5273 ERR_(accel)(" unknown reason - please report!\n");
5275 return TRUE;
5278 /**********************************************************************
5279 * TranslateAcceleratorA (USER32.@)
5280 * TranslateAccelerator (USER32.@)
5282 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5284 switch (msg->message)
5286 case WM_KEYDOWN:
5287 case WM_SYSKEYDOWN:
5288 return TranslateAcceleratorW( hWnd, hAccel, msg );
5290 case WM_CHAR:
5291 case WM_SYSCHAR:
5293 MSG msgW = *msg;
5294 char ch = LOWORD(msg->wParam);
5295 WCHAR wch;
5296 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5297 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5298 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5301 default:
5302 return 0;
5306 /**********************************************************************
5307 * TranslateAcceleratorW (USER32.@)
5309 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5311 ACCEL data[32], *ptr = data;
5312 int i, count;
5314 if (!hWnd) return 0;
5316 if (msg->message != WM_KEYDOWN &&
5317 msg->message != WM_SYSKEYDOWN &&
5318 msg->message != WM_CHAR &&
5319 msg->message != WM_SYSCHAR)
5320 return 0;
5322 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5323 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5325 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5326 if (count > sizeof(data)/sizeof(data[0]))
5328 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5330 count = CopyAcceleratorTableW( hAccel, ptr, count );
5331 for (i = 0; i < count; i++)
5333 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5334 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5335 break;
5337 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5338 return (i < count);