comctl32/tests: Fix some test failures on older comctl32.
[wine.git] / dlls / user32 / menu.c
blob230e412dc063f72977389a43d235661f087fe8fa
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/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
60 #include "win.h"
61 #include "controls.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu);
66 WINE_DECLARE_DEBUG_CHANNEL(accel);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
74 typedef struct {
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType; /* Item type. */
77 UINT fState; /* Item state. */
78 UINT_PTR wID; /* Item id. */
79 HMENU hSubMenu; /* Pop-up menu. */
80 HBITMAP hCheckBit; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
82 LPWSTR text; /* Item text. */
83 ULONG_PTR dwItemData; /* Application defined. */
84 LPWSTR dwTypeData; /* depends on fMask */
85 HBITMAP hbmpItem; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect; /* Item area (relative to menu window) */
88 UINT xTab; /* X position of text after Tab */
89 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 * bitmap */
91 } MENUITEM;
93 /* Popup menu structure */
94 typedef struct {
95 struct user_object obj;
96 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
97 WORD Width; /* Width of the whole menu */
98 WORD Height; /* Height of the whole menu */
99 UINT nItems; /* Number of items in the menu */
100 HWND hWnd; /* Window containing the menu */
101 MENUITEM *items; /* Array of menu items */
102 UINT FocusedItem; /* Currently focused item */
103 HWND hwndOwner; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling; /* Scroll arrows are active */
106 UINT nScrollPos; /* Current scroll position */
107 UINT nTotalHeight; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle; /* Extended menu style */
110 UINT cyMax; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack; /* brush for menu background */
112 DWORD dwContextHelpID;
113 DWORD dwMenuData; /* application defined value */
114 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
115 WORD textOffset; /* Offset of text when items have both bitmaps and text */
116 } POPUPMENU, *LPPOPUPMENU;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
124 typedef struct
126 UINT trackFlags;
127 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu; /* initial menu */
129 HWND hOwnerWnd; /* where notifications are sent */
130 POINT pt;
131 } MTRACKER;
133 #define MENU_MAGIC 0x554d /* 'MU' */
135 #define ITEM_PREV -1
136 #define ITEM_NEXT 1
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* maximum allowed depth of any branch in the menu tree.
151 * This value is slightly larger than in windows (25) to
152 * stay on the safe side. */
153 #define MAXMENUDEPTH 30
155 /* (other menu->FocusedItem values give the position of the focused item) */
156 #define NO_SELECTED_ITEM 0xffff
158 #define MENU_ITEM_TYPE(flags) \
159 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
161 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
162 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
163 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
165 #define IS_SYSTEM_MENU(menu) \
166 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
168 #define MENUITEMINFO_TYPE_MASK \
169 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
170 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
171 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
172 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
173 #define STATE_MASK (~TYPE_MASK)
174 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
176 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
178 static SIZE menucharsize;
179 static UINT ODitemheight; /* default owner drawn item height */
181 /* Use global popup window because there's no way 2 menus can
182 * be tracked at the same time. */
183 static HWND top_popup;
184 static HMENU top_popup_hmenu;
186 /* Flag set by EndMenu() to force an exit from menu tracking */
187 static BOOL fEndMenu = FALSE;
189 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
191 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
193 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
195 /*********************************************************************
196 * menu class descriptor
198 const struct builtin_class_descr MENU_builtin_class =
200 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
201 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
202 NULL, /* procA (winproc is Unicode only) */
203 PopupMenuWndProc, /* procW */
204 sizeof(HMENU), /* extra */
205 IDC_ARROW, /* cursor */
206 (HBRUSH)(COLOR_MENU+1) /* brush */
210 /***********************************************************************
211 * debug_print_menuitem
213 * Print a menuitem in readable form.
216 #define debug_print_menuitem(pre, mp, post) \
217 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
219 #define MENUOUT(text) \
220 TRACE("%s%s", (count++ ? "," : ""), (text))
222 #define MENUFLAG(bit,text) \
223 do { \
224 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
225 } while (0)
227 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
228 const char *postfix)
230 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
231 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
232 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
233 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
234 TRACE("%s ", prefix);
235 if (mp) {
236 UINT flags = mp->fType;
237 TRACE( "{ ID=0x%lx", mp->wID);
238 if ( mp->hSubMenu)
239 TRACE( ", Sub=%p", mp->hSubMenu);
240 if (flags) {
241 int count = 0;
242 TRACE( ", fType=");
243 MENUFLAG( MFT_SEPARATOR, "sep");
244 MENUFLAG( MFT_OWNERDRAW, "own");
245 MENUFLAG( MFT_BITMAP, "bit");
246 MENUFLAG(MF_POPUP, "pop");
247 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
248 MENUFLAG(MFT_MENUBREAK, "brk");
249 MENUFLAG(MFT_RADIOCHECK, "radio");
250 MENUFLAG(MFT_RIGHTORDER, "rorder");
251 MENUFLAG(MF_SYSMENU, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
253 if (flags)
254 TRACE( "+0x%x", flags);
256 flags = mp->fState;
257 if (flags) {
258 int count = 0;
259 TRACE( ", State=");
260 MENUFLAG(MFS_GRAYED, "grey");
261 MENUFLAG(MFS_DEFAULT, "default");
262 MENUFLAG(MFS_DISABLED, "dis");
263 MENUFLAG(MFS_CHECKED, "check");
264 MENUFLAG(MFS_HILITE, "hi");
265 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
266 MENUFLAG(MF_MOUSESELECT, "mouse");
267 if (flags)
268 TRACE( "+0x%x", flags);
270 if (mp->hCheckBit)
271 TRACE( ", Chk=%p", mp->hCheckBit);
272 if (mp->hUnCheckBit)
273 TRACE( ", Unc=%p", mp->hUnCheckBit);
274 if (mp->text)
275 TRACE( ", Text=%s", debugstr_w(mp->text));
276 if (mp->dwItemData)
277 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
278 if (mp->hbmpItem)
280 if( IS_MAGIC_BITMAP(mp->hbmpItem))
281 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
282 else
283 TRACE( ", hbitmap=%p", mp->hbmpItem);
285 TRACE( " }");
286 } else
287 TRACE( "NULL");
288 TRACE(" %s\n", postfix);
291 #undef MENUOUT
292 #undef MENUFLAG
295 /***********************************************************************
296 * MENU_GetMenu
298 * Validate the given menu handle and returns the menu structure pointer.
300 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
302 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
304 if (menu == OBJ_OTHER_PROCESS)
306 WARN( "other process menu %p?\n", hMenu);
307 return NULL;
309 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
310 else WARN("invalid menu handle=%p\n", hMenu);
311 return menu;
314 /***********************************************************************
315 * get_win_sys_menu
317 * Get the system menu of a window
319 static HMENU get_win_sys_menu( HWND hwnd )
321 HMENU ret = 0;
322 WND *win = WIN_GetPtr( hwnd );
323 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
325 ret = win->hSysMenu;
326 WIN_ReleasePtr( win );
328 return ret;
331 /***********************************************************************
332 * get_menu_font
334 static HFONT get_menu_font( BOOL bold )
336 static HFONT hMenuFont, hMenuFontBold;
338 HFONT ret = bold ? hMenuFontBold : hMenuFont;
340 if (!ret)
342 NONCLIENTMETRICSW ncm;
343 HFONT prev;
345 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
346 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
348 if (bold)
350 ncm.lfMenuFont.lfWeight += 300;
351 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
353 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
354 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
355 ret, NULL );
356 if (prev)
358 /* another thread beat us to it */
359 DeleteObject( ret );
360 ret = prev;
363 return ret;
366 /***********************************************************************
367 * get_arrow_bitmap
369 static HBITMAP get_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap;
373 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
374 return arrow_bitmap;
377 /***********************************************************************
378 * get_down_arrow_bitmap
380 static HBITMAP get_down_arrow_bitmap(void)
382 static HBITMAP arrow_bitmap;
384 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
385 return arrow_bitmap;
388 /***********************************************************************
389 * get_down_arrow_inactive_bitmap
391 static HBITMAP get_down_arrow_inactive_bitmap(void)
393 static HBITMAP arrow_bitmap;
395 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
396 return arrow_bitmap;
399 /***********************************************************************
400 * get_up_arrow_bitmap
402 static HBITMAP get_up_arrow_bitmap(void)
404 static HBITMAP arrow_bitmap;
406 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
407 return arrow_bitmap;
410 /***********************************************************************
411 * get_up_arrow_inactive_bitmap
413 static HBITMAP get_up_arrow_inactive_bitmap(void)
415 static HBITMAP arrow_bitmap;
417 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
418 return arrow_bitmap;
421 /***********************************************************************
422 * MENU_CopySysPopup
424 * Return the default system menu.
426 static HMENU MENU_CopySysPopup(void)
428 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
429 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
431 if( hMenu ) {
432 MENUINFO minfo;
433 MENUITEMINFOW miteminfo;
434 POPUPMENU* menu = MENU_GetMenu(hMenu);
435 menu->wFlags |= MF_SYSMENU | MF_POPUP;
436 /* decorate the menu with bitmaps */
437 minfo.cbSize = sizeof( MENUINFO);
438 minfo.dwStyle = MNS_CHECKORBMP;
439 minfo.fMask = MIM_STYLE;
440 SetMenuInfo( hMenu, &minfo);
441 miteminfo.cbSize = sizeof( MENUITEMINFOW);
442 miteminfo.fMask = MIIM_BITMAP;
443 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
444 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
445 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
446 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
447 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
448 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
449 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
450 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
451 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
453 else
454 ERR("Unable to load default system menu\n" );
456 TRACE("returning %p.\n", hMenu );
458 return hMenu;
462 /**********************************************************************
463 * MENU_GetSysMenu
465 * Create a copy of the system menu. System menu in Windows is
466 * a special menu bar with the single entry - system menu popup.
467 * This popup is presented to the outside world as a "system menu".
468 * However, the real system menu handle is sometimes seen in the
469 * WM_MENUSELECT parameters (and Word 6 likes it this way).
471 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
473 HMENU hMenu;
475 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
476 if ((hMenu = CreateMenu()))
478 POPUPMENU *menu = MENU_GetMenu(hMenu);
479 menu->wFlags = MF_SYSMENU;
480 menu->hWnd = WIN_GetFullHandle( hWnd );
481 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
483 if (!hPopupMenu)
484 hPopupMenu = MENU_CopySysPopup();
486 if (hPopupMenu)
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
499 return hMenu;
501 DestroyMenu( hMenu );
503 ERR("failed to load system menu!\n");
504 return 0;
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
515 BOOL gray;
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
529 /* The menu item must keep its state if it's disabled */
530 if(gray)
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
538 * HMENU hMenu )
540 *****************************************************************************/
542 static UINT MENU_GetStartOfNextColumn(
543 HMENU hMenu )
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 UINT i;
548 if(!menu)
549 return NO_SELECTED_ITEM;
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
553 return i;
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return i;
560 return NO_SELECTED_ITEM;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
567 * HMENU hMenu )
569 *****************************************************************************/
571 static UINT MENU_GetStartOfPrevColumn(
572 HMENU hMenu )
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 UINT i;
577 if( !menu )
578 return NO_SELECTED_ITEM;
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
583 /* Find the start of the column */
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 --i); /* empty */
589 if(i == 0)
590 return NO_SELECTED_ITEM;
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 break;
597 TRACE("ret %d.\n", i );
599 return i;
604 /***********************************************************************
605 * MENU_FindItem
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
612 POPUPMENU *menu;
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
615 UINT i;
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
623 else
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
628 if (item->fType & MF_POPUP)
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
632 if (subitem)
634 *hmenu = hsubmenu;
635 return subitem;
637 else if (item->wID == *nPos)
639 /* fallback to this item if nothing else found */
640 fallback_pos = i;
641 fallback = item;
644 else if (item->wID == *nPos)
646 *nPos = i;
647 return item;
652 if (fallback)
653 *nPos = fallback_pos;
655 return fallback;
658 /***********************************************************************
659 * MENU_FindSubMenu
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
667 POPUPMENU *menu;
668 UINT i;
669 MENUITEM *item;
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
673 item = menu->items;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
677 return i;
679 else {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
683 *hmenu = hsubmenu;
684 return pos;
688 return NO_SELECTED_ITEM;
691 /***********************************************************************
692 * MENU_FreeItemData
694 static void MENU_FreeItemData( MENUITEM* item )
696 /* delete text */
697 HeapFree( GetProcessHeap(), 0, item->text );
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
705 static void
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
708 if (menu->bScrolling)
710 UINT arrow_bitmap_height;
711 BITMAP bmp;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
731 MENUITEM *item;
732 UINT i;
733 RECT rect;
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 pt.x -= rect.left;
737 pt.y -= rect.top;
738 item = menu->items;
739 for (i = 0; i < menu->nItems; i++, item++)
741 rect = item->rect;
742 MENU_AdjustMenuItemRect(menu, &rect);
743 if (PtInRect(&rect, pt))
745 if (pos) *pos = i;
746 return item;
749 return NULL;
753 /***********************************************************************
754 * MENU_FindItemByKey
756 * Find the menu item selected by a key press.
757 * Return item id, -1 if none, -2 if we should close the menu.
759 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
760 WCHAR key, BOOL forceMenuChar )
762 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
764 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
766 if (hmenu)
768 POPUPMENU *menu = MENU_GetMenu( hmenu );
769 MENUITEM *item = menu->items;
770 LRESULT menuchar;
772 if( !forceMenuChar )
774 UINT i;
776 for (i = 0; i < menu->nItems; i++, item++)
778 if( item->text)
780 WCHAR *p = item->text - 2;
783 p = strchrW (p + 2, '&');
785 while (p != NULL && p [1] == '&');
786 if (p && (toupperW(p[1]) == toupperW(key))) return i;
790 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
791 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
792 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
793 if (HIWORD(menuchar) == 1) return (UINT)(-2);
795 return (UINT)(-1);
799 /***********************************************************************
800 * MENU_GetBitmapItemSize
802 * Get the size of a bitmap item.
804 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
805 HWND hwndOwner)
807 BITMAP bm;
808 HBITMAP bmp = lpitem->hbmpItem;
810 size->cx = size->cy = 0;
812 /* check if there is a magic menu item associated with this item */
813 switch( (INT_PTR) bmp )
815 case (INT_PTR)HBMMENU_CALLBACK:
817 MEASUREITEMSTRUCT measItem;
818 measItem.CtlType = ODT_MENU;
819 measItem.CtlID = 0;
820 measItem.itemID = lpitem->wID;
821 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
822 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
823 measItem.itemData = lpitem->dwItemData;
824 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
825 size->cx = measItem.itemWidth;
826 size->cy = measItem.itemHeight;
827 return;
829 break;
830 case (INT_PTR)HBMMENU_SYSTEM:
831 if (lpitem->dwItemData)
833 bmp = (HBITMAP)lpitem->dwItemData;
834 break;
836 /* fall through */
837 case (INT_PTR)HBMMENU_MBAR_RESTORE:
838 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
839 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
840 case (INT_PTR)HBMMENU_MBAR_CLOSE:
841 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
842 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
843 size->cy = size->cx;
844 return;
845 case (INT_PTR)HBMMENU_POPUP_CLOSE:
846 case (INT_PTR)HBMMENU_POPUP_RESTORE:
847 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
848 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
849 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
850 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
851 return;
853 if (GetObjectW(bmp, sizeof(bm), &bm ))
855 size->cx = bm.bmWidth;
856 size->cy = bm.bmHeight;
860 /***********************************************************************
861 * MENU_DrawBitmapItem
863 * Draw a bitmap item.
865 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
866 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
868 BITMAP bm;
869 DWORD rop;
870 HDC hdcMem;
871 HBITMAP bmp;
872 int w = rect->right - rect->left;
873 int h = rect->bottom - rect->top;
874 int bmp_xoffset = 0;
875 int left, top;
876 HBITMAP hbmToDraw = lpitem->hbmpItem;
877 bmp = hbmToDraw;
879 /* Check if there is a magic menu item associated with this item */
880 if (IS_MAGIC_BITMAP(hbmToDraw))
882 UINT flags = 0;
883 WCHAR bmchr = 0;
884 RECT r;
886 switch((INT_PTR)hbmToDraw)
888 case (INT_PTR)HBMMENU_SYSTEM:
889 if (lpitem->dwItemData)
891 bmp = (HBITMAP)lpitem->dwItemData;
892 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
894 else
896 static HBITMAP hBmpSysMenu;
898 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
899 bmp = hBmpSysMenu;
900 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
901 /* only use right half of the bitmap */
902 bmp_xoffset = bm.bmWidth / 2;
903 bm.bmWidth -= bmp_xoffset;
905 goto got_bitmap;
906 case (INT_PTR)HBMMENU_MBAR_RESTORE:
907 flags = DFCS_CAPTIONRESTORE;
908 break;
909 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
910 flags = DFCS_CAPTIONMIN;
911 break;
912 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
913 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
914 break;
915 case (INT_PTR)HBMMENU_MBAR_CLOSE:
916 flags = DFCS_CAPTIONCLOSE;
917 break;
918 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
919 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
920 break;
921 case (INT_PTR)HBMMENU_CALLBACK:
923 DRAWITEMSTRUCT drawItem;
924 drawItem.CtlType = ODT_MENU;
925 drawItem.CtlID = 0;
926 drawItem.itemID = lpitem->wID;
927 drawItem.itemAction = odaction;
928 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
929 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
930 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
931 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
932 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
933 drawItem.hwndItem = (HWND)hmenu;
934 drawItem.hDC = hdc;
935 drawItem.itemData = lpitem->dwItemData;
936 drawItem.rcItem = *rect;
937 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
938 return;
940 break;
941 case (INT_PTR)HBMMENU_POPUP_CLOSE:
942 bmchr = 0x72;
943 break;
944 case (INT_PTR)HBMMENU_POPUP_RESTORE:
945 bmchr = 0x32;
946 break;
947 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
948 bmchr = 0x31;
949 break;
950 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
951 bmchr = 0x30;
952 break;
953 default:
954 FIXME("Magic %p not implemented\n", hbmToDraw);
955 return;
957 if (bmchr)
959 /* draw the magic bitmaps using marlett font characters */
960 /* FIXME: fontsize and the position (x,y) could probably be better */
961 HFONT hfont, hfontsav;
962 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
963 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
964 { 'M','a','r','l','e','t','t',0 } };
965 logfont.lfHeight = min( h, w) - 5 ;
966 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
967 hfont = CreateFontIndirectW( &logfont);
968 hfontsav = SelectObject(hdc, hfont);
969 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
970 SelectObject(hdc, hfontsav);
971 DeleteObject( hfont);
973 else
975 r = *rect;
976 InflateRect( &r, -1, -1 );
977 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
978 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
980 return;
983 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
985 got_bitmap:
986 hdcMem = CreateCompatibleDC( hdc );
987 SelectObject( hdcMem, bmp );
989 /* handle fontsize > bitmap_height */
990 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
991 left=rect->left;
992 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
993 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
994 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
995 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
996 DeleteDC( hdcMem );
1000 /***********************************************************************
1001 * MENU_CalcItemSize
1003 * Calculate the size of the menu item and store it in lpitem->rect.
1005 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1006 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1008 WCHAR *p;
1009 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1010 UINT arrow_bitmap_width;
1011 BITMAP bm;
1012 INT itemheight;
1014 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1015 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1016 (menuBar ? " (MenuBar)" : ""));
1018 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1019 arrow_bitmap_width = bm.bmWidth;
1021 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1022 if( !menucharsize.cx ) {
1023 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1024 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1025 * but it is unlikely an application will depend on that */
1026 ODitemheight = HIWORD( GetDialogBaseUnits());
1029 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1031 if (lpitem->fType & MF_OWNERDRAW)
1033 MEASUREITEMSTRUCT mis;
1034 mis.CtlType = ODT_MENU;
1035 mis.CtlID = 0;
1036 mis.itemID = lpitem->wID;
1037 mis.itemData = lpitem->dwItemData;
1038 mis.itemHeight = ODitemheight;
1039 mis.itemWidth = 0;
1040 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1041 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1042 * width of a menufont character to the width of an owner-drawn menu.
1044 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1045 if (menuBar) {
1046 /* under at least win95 you seem to be given a standard
1047 height for the menu and the height value is ignored */
1048 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1049 } else
1050 lpitem->rect.bottom += mis.itemHeight;
1052 TRACE("id=%04lx size=%dx%d\n",
1053 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1054 lpitem->rect.bottom-lpitem->rect.top);
1055 return;
1058 if (lpitem->fType & MF_SEPARATOR)
1060 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1061 if( !menuBar)
1062 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1063 return;
1066 itemheight = 0;
1067 lpitem->xTab = 0;
1069 if (!menuBar) {
1070 if (lpitem->hbmpItem) {
1071 SIZE size;
1073 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1074 /* Keep the size of the bitmap in callback mode to be able
1075 * to draw it correctly */
1076 lpitem->bmpsize = size;
1077 lppop->textOffset = max( lppop->textOffset, size.cx);
1078 lpitem->rect.right += size.cx + 2;
1079 itemheight = size.cy + 2;
1081 if( !(lppop->dwStyle & MNS_NOCHECK))
1082 lpitem->rect.right += check_bitmap_width;
1083 lpitem->rect.right += 4 + menucharsize.cx;
1084 lpitem->xTab = lpitem->rect.right;
1085 lpitem->rect.right += arrow_bitmap_width;
1086 } else if (lpitem->hbmpItem) { /* menuBar */
1087 SIZE size;
1089 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1090 lpitem->bmpsize = size;
1091 lpitem->rect.right += size.cx;
1092 if( lpitem->text) lpitem->rect.right += 2;
1093 itemheight = size.cy;
1096 /* it must be a text item - unless it's the system menu */
1097 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1098 HFONT hfontOld = NULL;
1099 RECT rc = lpitem->rect;
1100 LONG txtheight, txtwidth;
1102 if ( lpitem->fState & MFS_DEFAULT ) {
1103 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1105 if (menuBar) {
1106 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1107 DT_SINGLELINE|DT_CALCRECT);
1108 lpitem->rect.right += rc.right - rc.left;
1109 itemheight = max( max( itemheight, txtheight),
1110 GetSystemMetrics( SM_CYMENU) - 1);
1111 lpitem->rect.right += 2 * menucharsize.cx;
1112 } else {
1113 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1114 RECT tmprc = rc;
1115 LONG tmpheight;
1116 int n = (int)( p - lpitem->text);
1117 /* Item contains a tab (only meaningful in popup menus) */
1118 /* get text size before the tab */
1119 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1120 DT_SINGLELINE|DT_CALCRECT);
1121 txtwidth = rc.right - rc.left;
1122 p += 1; /* advance past the Tab */
1123 /* get text size after the tab */
1124 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1125 DT_SINGLELINE|DT_CALCRECT);
1126 lpitem->xTab += txtwidth;
1127 txtheight = max( txtheight, tmpheight);
1128 txtwidth += menucharsize.cx + /* space for the tab */
1129 tmprc.right - tmprc.left; /* space for the short cut */
1130 } else {
1131 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1132 DT_SINGLELINE|DT_CALCRECT);
1133 txtwidth = rc.right - rc.left;
1134 lpitem->xTab += txtwidth;
1136 lpitem->rect.right += 2 + txtwidth;
1137 itemheight = max( itemheight,
1138 max( txtheight + 2, menucharsize.cy + 4));
1140 if (hfontOld) SelectObject (hdc, hfontOld);
1141 } else if( menuBar) {
1142 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1144 lpitem->rect.bottom += itemheight;
1145 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1149 /***********************************************************************
1150 * MENU_GetMaxPopupHeight
1152 static UINT
1153 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1155 if (lppop->cyMax)
1156 return lppop->cyMax;
1157 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1161 /***********************************************************************
1162 * MENU_PopupMenuCalcSize
1164 * Calculate the size of a popup menu.
1166 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1168 MENUITEM *lpitem;
1169 HDC hdc;
1170 UINT start, i;
1171 int textandbmp = FALSE;
1172 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1174 lppop->Width = lppop->Height = 0;
1175 if (lppop->nItems == 0) return;
1176 hdc = GetDC( 0 );
1178 SelectObject( hdc, get_menu_font(FALSE));
1180 start = 0;
1181 maxX = 2 + 1;
1183 lppop->textOffset = 0;
1185 while (start < lppop->nItems)
1187 lpitem = &lppop->items[start];
1188 orgX = maxX;
1189 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1190 orgX += MENU_COL_SPACE;
1191 orgY = MENU_TOP_MARGIN;
1193 maxTab = maxTabWidth = 0;
1194 /* Parse items until column break or end of menu */
1195 for (i = start; i < lppop->nItems; i++, lpitem++)
1197 if ((i != start) &&
1198 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1200 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1201 maxX = max( maxX, lpitem->rect.right );
1202 orgY = lpitem->rect.bottom;
1203 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1205 maxTab = max( maxTab, lpitem->xTab );
1206 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1208 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1211 /* Finish the column (set all items to the largest width found) */
1212 maxX = max( maxX, maxTab + maxTabWidth );
1213 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1215 lpitem->rect.right = maxX;
1216 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1217 lpitem->xTab = maxTab;
1220 lppop->Height = max( lppop->Height, orgY );
1223 lppop->Width = maxX;
1224 /* if none of the items have both text and bitmap then
1225 * the text and bitmaps are all aligned on the left. If there is at
1226 * least one item with both text and bitmap then bitmaps are
1227 * on the left and texts left aligned with the right hand side
1228 * of the bitmaps */
1229 if( !textandbmp) lppop->textOffset = 0;
1231 /* space for 3d border */
1232 lppop->Height += MENU_BOTTOM_MARGIN;
1233 lppop->Width += 2;
1235 /* Adjust popup height if it exceeds maximum */
1236 maxHeight = MENU_GetMaxPopupHeight(lppop);
1237 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1238 if (lppop->Height >= maxHeight)
1240 lppop->Height = maxHeight;
1241 lppop->bScrolling = TRUE;
1243 else
1245 lppop->bScrolling = FALSE;
1248 ReleaseDC( 0, hdc );
1252 /***********************************************************************
1253 * MENU_MenuBarCalcSize
1255 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1256 * height is off by 1 pixel which causes lengthy window relocations when
1257 * active document window is maximized/restored.
1259 * Calculate the size of the menu bar.
1261 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1262 LPPOPUPMENU lppop, HWND hwndOwner )
1264 MENUITEM *lpitem;
1265 UINT start, i, helpPos;
1266 int orgX, orgY, maxY;
1268 if ((lprect == NULL) || (lppop == NULL)) return;
1269 if (lppop->nItems == 0) return;
1270 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1271 lppop->Width = lprect->right - lprect->left;
1272 lppop->Height = 0;
1273 maxY = lprect->top+1;
1274 start = 0;
1275 helpPos = ~0U;
1276 lppop->textOffset = 0;
1277 while (start < lppop->nItems)
1279 lpitem = &lppop->items[start];
1280 orgX = lprect->left;
1281 orgY = maxY;
1283 /* Parse items until line break or end of menu */
1284 for (i = start; i < lppop->nItems; i++, lpitem++)
1286 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1287 if ((i != start) &&
1288 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1290 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1291 debug_print_menuitem (" item: ", lpitem, "");
1292 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1294 if (lpitem->rect.right > lprect->right)
1296 if (i != start) break;
1297 else lpitem->rect.right = lprect->right;
1299 maxY = max( maxY, lpitem->rect.bottom );
1300 orgX = lpitem->rect.right;
1303 /* Finish the line (set all items to the largest height found) */
1304 while (start < i) lppop->items[start++].rect.bottom = maxY;
1307 lprect->bottom = maxY;
1308 lppop->Height = lprect->bottom - lprect->top;
1310 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1311 /* the last item (if several lines, only move the last line) */
1312 if (helpPos == ~0U) return;
1313 lpitem = &lppop->items[lppop->nItems-1];
1314 orgY = lpitem->rect.top;
1315 orgX = lprect->right;
1316 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1317 if (lpitem->rect.top != orgY) break; /* Other line */
1318 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1319 lpitem->rect.left += orgX - lpitem->rect.right;
1320 lpitem->rect.right = orgX;
1321 orgX = lpitem->rect.left;
1326 /***********************************************************************
1327 * MENU_DrawScrollArrows
1329 * Draw scroll arrows.
1331 static void
1332 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1334 HDC hdcMem = CreateCompatibleDC(hdc);
1335 HBITMAP hOrigBitmap;
1336 UINT arrow_bitmap_width, arrow_bitmap_height;
1337 BITMAP bmp;
1338 RECT rect;
1340 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1341 arrow_bitmap_width = bmp.bmWidth;
1342 arrow_bitmap_height = bmp.bmHeight;
1345 if (lppop->nScrollPos)
1346 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1347 else
1348 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1349 rect.left = 0;
1350 rect.top = 0;
1351 rect.right = lppop->Width;
1352 rect.bottom = arrow_bitmap_height;
1353 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1354 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1355 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1356 rect.top = lppop->Height - arrow_bitmap_height;
1357 rect.bottom = lppop->Height;
1358 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1359 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1360 SelectObject(hdcMem, get_down_arrow_bitmap());
1361 else
1362 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1363 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1364 lppop->Height - arrow_bitmap_height,
1365 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1366 SelectObject(hdcMem, hOrigBitmap);
1367 DeleteDC(hdcMem);
1371 /***********************************************************************
1372 * draw_popup_arrow
1374 * Draws the popup-menu arrow.
1376 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1377 UINT arrow_bitmap_height)
1379 HDC hdcMem = CreateCompatibleDC( hdc );
1380 HBITMAP hOrigBitmap;
1382 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1383 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1384 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1385 arrow_bitmap_width, arrow_bitmap_height,
1386 hdcMem, 0, 0, SRCCOPY );
1387 SelectObject( hdcMem, hOrigBitmap );
1388 DeleteDC( hdcMem );
1390 /***********************************************************************
1391 * MENU_DrawMenuItem
1393 * Draw a single menu item.
1395 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1396 UINT height, BOOL menuBar, UINT odaction )
1398 RECT rect;
1399 BOOL flat_menu = FALSE;
1400 int bkgnd;
1401 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1402 POPUPMENU *menu = MENU_GetMenu(hmenu);
1403 RECT bmprc;
1405 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1407 if (!menuBar) {
1408 BITMAP bmp;
1409 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1410 arrow_bitmap_width = bmp.bmWidth;
1411 arrow_bitmap_height = bmp.bmHeight;
1414 if (lpitem->fType & MF_SYSMENU)
1416 if( !IsIconic(hwnd) )
1417 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1418 return;
1421 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1422 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1424 /* Setup colors */
1426 if (lpitem->fState & MF_HILITE)
1428 if(menuBar && !flat_menu) {
1429 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1430 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1431 } else {
1432 if(lpitem->fState & MF_GRAYED)
1433 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1434 else
1435 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1436 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1439 else
1441 if (lpitem->fState & MF_GRAYED)
1442 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1443 else
1444 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1445 SetBkColor( hdc, GetSysColor( bkgnd ) );
1448 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1449 rect = lpitem->rect;
1450 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1452 if (lpitem->fType & MF_OWNERDRAW)
1455 ** Experimentation under Windows reveals that an owner-drawn
1456 ** menu is given the rectangle which includes the space it requested
1457 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1458 ** and a popup-menu arrow. This is the value of lpitem->rect.
1459 ** Windows will leave all drawing to the application except for
1460 ** the popup-menu arrow. Windows always draws that itself, after
1461 ** the menu owner has finished drawing.
1463 DRAWITEMSTRUCT dis;
1465 dis.CtlType = ODT_MENU;
1466 dis.CtlID = 0;
1467 dis.itemID = lpitem->wID;
1468 dis.itemData = lpitem->dwItemData;
1469 dis.itemState = 0;
1470 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1471 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1472 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1473 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1474 dis.hwndItem = (HWND)hmenu;
1475 dis.hDC = hdc;
1476 dis.rcItem = rect;
1477 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1478 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1479 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1480 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1481 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1482 /* Draw the popup-menu arrow */
1483 if (lpitem->fType & MF_POPUP)
1484 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1485 arrow_bitmap_height);
1486 return;
1489 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1491 if (lpitem->fState & MF_HILITE)
1493 if (flat_menu)
1495 InflateRect (&rect, -1, -1);
1496 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1497 InflateRect (&rect, 1, 1);
1498 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1500 else
1502 if(menuBar)
1503 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1504 else
1505 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1508 else
1509 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1511 SetBkMode( hdc, TRANSPARENT );
1513 /* vertical separator */
1514 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1516 HPEN oldPen;
1517 RECT rc = rect;
1519 rc.left -= MENU_COL_SPACE / 2 + 1;
1520 rc.top = 3;
1521 rc.bottom = height - 3;
1522 if (flat_menu)
1524 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1525 MoveToEx( hdc, rc.left, rc.top, NULL );
1526 LineTo( hdc, rc.left, rc.bottom );
1527 SelectObject( hdc, oldPen );
1529 else
1530 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1533 /* horizontal separator */
1534 if (lpitem->fType & MF_SEPARATOR)
1536 HPEN oldPen;
1537 RECT rc = rect;
1539 rc.left++;
1540 rc.right--;
1541 rc.top = ( rc.top + rc.bottom) / 2;
1542 if (flat_menu)
1544 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1545 MoveToEx( hdc, rc.left, rc.top, NULL );
1546 LineTo( hdc, rc.right, rc.top );
1547 SelectObject( hdc, oldPen );
1549 else
1550 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1551 return;
1554 /* helper lines for debugging */
1555 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1556 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1557 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1558 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1561 if (lpitem->hbmpItem) {
1562 /* calculate the bitmap rectangle in coordinates relative
1563 * to the item rectangle */
1564 if( menuBar) {
1565 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1566 bmprc.left = 3;
1567 else
1568 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1570 else if (menu->dwStyle & MNS_NOCHECK)
1571 bmprc.left = 4;
1572 else if (menu->dwStyle & MNS_CHECKORBMP)
1573 bmprc.left = 2;
1574 else
1575 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1576 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1577 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1578 bmprc.top = 0;
1579 else
1580 bmprc.top = (rect.bottom - rect.top -
1581 lpitem->bmpsize.cy) / 2;
1582 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1585 if (!menuBar)
1587 HBITMAP bm;
1588 INT y = rect.top + rect.bottom;
1589 RECT rc = rect;
1590 int checked = FALSE;
1591 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1592 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1593 /* Draw the check mark
1595 * FIXME:
1596 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1598 if( !(menu->dwStyle & MNS_NOCHECK)) {
1599 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1600 lpitem->hUnCheckBit;
1601 if (bm) /* we have a custom bitmap */
1603 HDC hdcMem = CreateCompatibleDC( hdc );
1605 SelectObject( hdcMem, bm );
1606 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1607 check_bitmap_width, check_bitmap_height,
1608 hdcMem, 0, 0, SRCCOPY );
1609 DeleteDC( hdcMem );
1610 checked = TRUE;
1612 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1614 RECT r;
1615 HBITMAP bm = CreateBitmap( check_bitmap_width,
1616 check_bitmap_height, 1, 1, NULL );
1617 HDC hdcMem = CreateCompatibleDC( hdc );
1619 SelectObject( hdcMem, bm );
1620 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1621 DrawFrameControl( hdcMem, &r, DFC_MENU,
1622 (lpitem->fType & MFT_RADIOCHECK) ?
1623 DFCS_MENUBULLET : DFCS_MENUCHECK );
1624 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1625 hdcMem, 0, 0, SRCCOPY );
1626 DeleteDC( hdcMem );
1627 DeleteObject( bm );
1628 checked = TRUE;
1631 if( lpitem->hbmpItem &&
1632 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1633 POINT origorg;
1634 /* some applications make this assumption on the DC's origin */
1635 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1636 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1637 odaction, FALSE);
1638 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1640 /* Draw the popup-menu arrow */
1641 if (lpitem->fType & MF_POPUP)
1642 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1643 arrow_bitmap_height);
1644 rect.left += 4;
1645 if( !(menu->dwStyle & MNS_NOCHECK))
1646 rect.left += check_bitmap_width;
1647 rect.right -= arrow_bitmap_width;
1649 else if( lpitem->hbmpItem)
1650 { /* Draw the bitmap */
1651 POINT origorg;
1653 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1654 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1655 odaction, menuBar);
1656 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1658 /* process text if present */
1659 if (lpitem->text)
1661 register int i;
1662 HFONT hfontOld = 0;
1664 UINT uFormat = (menuBar) ?
1665 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1666 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1668 if( !(menu->dwStyle & MNS_CHECKORBMP))
1669 rect.left += menu->textOffset;
1671 if ( lpitem->fState & MFS_DEFAULT )
1673 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1676 if (menuBar) {
1677 if( lpitem->hbmpItem)
1678 rect.left += lpitem->bmpsize.cx;
1679 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1680 rect.left += menucharsize.cx;
1681 rect.right -= menucharsize.cx;
1684 for (i = 0; lpitem->text[i]; i++)
1685 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1686 break;
1688 if(lpitem->fState & MF_GRAYED)
1690 if (!(lpitem->fState & MF_HILITE) )
1692 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1693 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1694 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1695 --rect.left; --rect.top; --rect.right; --rect.bottom;
1697 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1700 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1702 /* paint the shortcut text */
1703 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1705 if (lpitem->text[i] == '\t')
1707 rect.left = lpitem->xTab;
1708 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1710 else
1712 rect.right = lpitem->xTab;
1713 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1716 if(lpitem->fState & MF_GRAYED)
1718 if (!(lpitem->fState & MF_HILITE) )
1720 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1721 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1722 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1723 --rect.left; --rect.top; --rect.right; --rect.bottom;
1725 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1727 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1730 if (hfontOld)
1731 SelectObject (hdc, hfontOld);
1736 /***********************************************************************
1737 * MENU_DrawPopupMenu
1739 * Paint a popup menu.
1741 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1743 HBRUSH hPrevBrush = 0;
1744 RECT rect;
1746 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1748 GetClientRect( hwnd, &rect );
1750 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1751 && (SelectObject( hdc, get_menu_font(FALSE))))
1753 HPEN hPrevPen;
1755 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1757 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1758 if( hPrevPen )
1760 POPUPMENU *menu;
1761 BOOL flat_menu = FALSE;
1763 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1764 if (flat_menu)
1765 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1766 else
1767 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1769 if( (menu = MENU_GetMenu( hmenu )))
1771 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1772 /* draw menu items */
1773 if( menu->nItems)
1775 MENUITEM *item;
1776 UINT u;
1778 item = menu->items;
1779 for( u = menu->nItems; u > 0; u--, item++)
1780 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1781 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1783 /* draw scroll arrows */
1784 if (menu->bScrolling)
1785 MENU_DrawScrollArrows(menu, hdc);
1787 } else
1789 SelectObject( hdc, hPrevBrush );
1794 /***********************************************************************
1795 * MENU_DrawMenuBar
1797 * Paint a menu bar. Returns the height of the menu bar.
1798 * called from [windows/nonclient.c]
1800 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1801 BOOL suppress_draw)
1803 LPPOPUPMENU lppop;
1804 HFONT hfontOld = 0;
1805 HMENU hMenu = GetMenu(hwnd);
1807 lppop = MENU_GetMenu( hMenu );
1808 if (lppop == NULL || lprect == NULL)
1810 return GetSystemMetrics(SM_CYMENU);
1813 if (suppress_draw)
1815 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1817 if (lppop->Height == 0)
1818 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1820 lprect->bottom = lprect->top + lppop->Height;
1822 if (hfontOld) SelectObject( hDC, hfontOld);
1823 return lppop->Height;
1825 else
1826 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1830 /***********************************************************************
1831 * MENU_ShowPopup
1833 * Display a popup menu.
1835 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1836 INT x, INT y, INT xanchor, INT yanchor )
1838 POPUPMENU *menu;
1839 INT width, height;
1840 POINT pt;
1841 HMONITOR monitor;
1842 MONITORINFO info;
1844 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1845 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1847 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1848 if (menu->FocusedItem != NO_SELECTED_ITEM)
1850 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1851 menu->FocusedItem = NO_SELECTED_ITEM;
1854 /* store the owner for DrawItem */
1855 menu->hwndOwner = hwndOwner;
1857 menu->nScrollPos = 0;
1858 MENU_PopupMenuCalcSize( menu );
1860 /* adjust popup menu pos so that it fits within the desktop */
1862 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1863 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1865 /* FIXME: should use item rect */
1866 pt.x = x;
1867 pt.y = y;
1868 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1869 info.cbSize = sizeof(info);
1870 GetMonitorInfoW( monitor, &info );
1872 if( flags & TPM_RIGHTALIGN ) x -= width;
1873 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1875 if( flags & TPM_BOTTOMALIGN ) y -= height;
1876 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1878 if( x + width > info.rcWork.right)
1880 if( xanchor && x >= width - xanchor )
1881 x -= width - xanchor;
1883 if( x + width > info.rcWork.right)
1884 x = info.rcWork.right - width;
1886 if( x < info.rcWork.left ) x = info.rcWork.left;
1888 if( y + height > info.rcWork.bottom)
1890 if( yanchor && y >= height + yanchor )
1891 y -= height + yanchor;
1893 if( y + height > info.rcWork.bottom)
1894 y = info.rcWork.bottom - height;
1896 if( y < info.rcWork.top ) y = info.rcWork.top;
1898 /* NOTE: In Windows, top menu popup is not owned. */
1899 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1900 WS_POPUP, x, y, width, height,
1901 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1902 (LPVOID)hmenu );
1903 if( !menu->hWnd ) return FALSE;
1904 if (!top_popup) {
1905 top_popup = menu->hWnd;
1906 top_popup_hmenu = hmenu;
1908 /* Display the window */
1910 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1911 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1912 UpdateWindow( menu->hWnd );
1913 return TRUE;
1917 /***********************************************************************
1918 * MENU_EnsureMenuItemVisible
1920 static void
1921 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1923 if (lppop->bScrolling)
1925 MENUITEM *item = &lppop->items[wIndex];
1926 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1927 UINT nOldPos = lppop->nScrollPos;
1928 RECT rc;
1929 UINT arrow_bitmap_height;
1930 BITMAP bmp;
1932 GetClientRect(lppop->hWnd, &rc);
1934 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1935 arrow_bitmap_height = bmp.bmHeight;
1937 rc.top += arrow_bitmap_height;
1938 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1940 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1941 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1944 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1945 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1946 MENU_DrawScrollArrows(lppop, hdc);
1948 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1950 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1951 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1952 MENU_DrawScrollArrows(lppop, hdc);
1958 /***********************************************************************
1959 * MENU_SelectItem
1961 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1962 BOOL sendMenuSelect, HMENU topmenu )
1964 LPPOPUPMENU lppop;
1965 HDC hdc;
1967 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1969 lppop = MENU_GetMenu( hmenu );
1970 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1972 if (lppop->FocusedItem == wIndex) return;
1973 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1974 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1975 if (!top_popup) {
1976 top_popup = lppop->hWnd;
1977 top_popup_hmenu = hmenu;
1980 SelectObject( hdc, get_menu_font(FALSE));
1982 /* Clear previous highlighted item */
1983 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1985 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1986 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1987 lppop->Height, !(lppop->wFlags & MF_POPUP),
1988 ODA_SELECT );
1991 /* Highlight new item (if any) */
1992 lppop->FocusedItem = wIndex;
1993 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1995 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1996 lppop->items[wIndex].fState |= MF_HILITE;
1997 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1998 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1999 &lppop->items[wIndex], lppop->Height,
2000 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2002 if (sendMenuSelect)
2004 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2005 SendMessageW( hwndOwner, WM_MENUSELECT,
2006 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2007 ip->fType | ip->fState |
2008 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2011 else if (sendMenuSelect) {
2012 if(topmenu){
2013 int pos;
2014 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2015 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2016 MENUITEM *ip = &ptm->items[pos];
2017 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2018 ip->fType | ip->fState |
2019 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2023 ReleaseDC( lppop->hWnd, hdc );
2027 /***********************************************************************
2028 * MENU_MoveSelection
2030 * Moves currently selected item according to the offset parameter.
2031 * If there is no selection then it should select the last item if
2032 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2034 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2036 INT i;
2037 POPUPMENU *menu;
2039 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2041 menu = MENU_GetMenu( hmenu );
2042 if ((!menu) || (!menu->items)) return;
2044 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2046 if( menu->nItems == 1 ) return; else
2047 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2048 ; i += offset)
2049 if (!(menu->items[i].fType & MF_SEPARATOR))
2051 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2052 return;
2056 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2057 i >= 0 && i < menu->nItems ; i += offset)
2058 if (!(menu->items[i].fType & MF_SEPARATOR))
2060 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2061 return;
2066 /**********************************************************************
2067 * MENU_InsertItem
2069 * Insert (allocate) a new item into a menu.
2071 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2073 MENUITEM *newItems;
2074 POPUPMENU *menu;
2076 if (!(menu = MENU_GetMenu(hMenu)))
2077 return NULL;
2079 /* Find where to insert new item */
2081 if (flags & MF_BYPOSITION) {
2082 if (pos > menu->nItems)
2083 pos = menu->nItems;
2084 } else {
2085 if (!MENU_FindItem( &hMenu, &pos, flags ))
2086 pos = menu->nItems;
2087 else {
2088 if (!(menu = MENU_GetMenu( hMenu )))
2089 return NULL;
2093 /* Make sure that MDI system buttons stay on the right side.
2094 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2095 * regardless of their id.
2097 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2098 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2099 pos--;
2101 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2103 /* Create new items array */
2105 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2106 if (!newItems)
2108 WARN("allocation failed\n" );
2109 return NULL;
2111 if (menu->nItems > 0)
2113 /* Copy the old array into the new one */
2114 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2115 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2116 (menu->nItems-pos)*sizeof(MENUITEM) );
2117 HeapFree( GetProcessHeap(), 0, menu->items );
2119 menu->items = newItems;
2120 menu->nItems++;
2121 memset( &newItems[pos], 0, sizeof(*newItems) );
2122 menu->Height = 0; /* force size recalculate */
2123 return &newItems[pos];
2127 /**********************************************************************
2128 * MENU_ParseResource
2130 * Parse a standard menu resource and add items to the menu.
2131 * Return a pointer to the end of the resource.
2133 * NOTE: flags is equivalent to the mtOption field
2135 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2137 WORD flags, id = 0;
2138 LPCSTR str;
2139 BOOL end_flag;
2143 flags = GET_WORD(res);
2144 end_flag = flags & MF_END;
2145 /* Remove MF_END because it has the same value as MF_HILITE */
2146 flags &= ~MF_END;
2147 res += sizeof(WORD);
2148 if (!(flags & MF_POPUP))
2150 id = GET_WORD(res);
2151 res += sizeof(WORD);
2153 str = res;
2154 if (!unicode) res += strlen(str) + 1;
2155 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2156 if (flags & MF_POPUP)
2158 HMENU hSubMenu = CreatePopupMenu();
2159 if (!hSubMenu) return NULL;
2160 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2161 return NULL;
2162 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2163 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2165 else /* Not a popup */
2167 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2168 else AppendMenuW( hMenu, flags, id,
2169 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2171 } while (!end_flag);
2172 return res;
2176 /**********************************************************************
2177 * MENUEX_ParseResource
2179 * Parse an extended menu resource and add items to the menu.
2180 * Return a pointer to the end of the resource.
2182 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2184 WORD resinfo;
2185 do {
2186 MENUITEMINFOW mii;
2188 mii.cbSize = sizeof(mii);
2189 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2190 mii.fType = GET_DWORD(res);
2191 res += sizeof(DWORD);
2192 mii.fState = GET_DWORD(res);
2193 res += sizeof(DWORD);
2194 mii.wID = GET_DWORD(res);
2195 res += sizeof(DWORD);
2196 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2197 res += sizeof(WORD);
2198 /* Align the text on a word boundary. */
2199 res += (~((UINT_PTR)res - 1)) & 1;
2200 mii.dwTypeData = (LPWSTR) res;
2201 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2202 /* Align the following fields on a dword boundary. */
2203 res += (~((UINT_PTR)res - 1)) & 3;
2205 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2206 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2208 if (resinfo & 1) { /* Pop-up? */
2209 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2210 res += sizeof(DWORD);
2211 mii.hSubMenu = CreatePopupMenu();
2212 if (!mii.hSubMenu)
2213 return NULL;
2214 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2215 DestroyMenu(mii.hSubMenu);
2216 return NULL;
2218 mii.fMask |= MIIM_SUBMENU;
2219 mii.fType |= MF_POPUP;
2221 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2223 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2224 mii.wID, mii.fType);
2225 mii.fType |= MF_SEPARATOR;
2227 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2228 } while (!(resinfo & MF_END));
2229 return res;
2233 /***********************************************************************
2234 * MENU_GetSubPopup
2236 * Return the handle of the selected sub-popup menu (if any).
2238 static HMENU MENU_GetSubPopup( HMENU hmenu )
2240 POPUPMENU *menu;
2241 MENUITEM *item;
2243 menu = MENU_GetMenu( hmenu );
2245 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2247 item = &menu->items[menu->FocusedItem];
2248 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2249 return item->hSubMenu;
2250 return 0;
2254 /***********************************************************************
2255 * MENU_HideSubPopups
2257 * Hide the sub-popup menus of this menu.
2259 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2260 BOOL sendMenuSelect, UINT wFlags )
2262 POPUPMENU *menu = MENU_GetMenu( hmenu );
2264 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2266 if (menu && top_popup)
2268 HMENU hsubmenu;
2269 POPUPMENU *submenu;
2270 MENUITEM *item;
2272 if (menu->FocusedItem != NO_SELECTED_ITEM)
2274 item = &menu->items[menu->FocusedItem];
2275 if (!(item->fType & MF_POPUP) ||
2276 !(item->fState & MF_MOUSESELECT)) return;
2277 item->fState &= ~MF_MOUSESELECT;
2278 hsubmenu = item->hSubMenu;
2279 } else return;
2281 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2282 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2283 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2284 DestroyWindow( submenu->hWnd );
2285 submenu->hWnd = 0;
2287 if (!(wFlags & TPM_NONOTIFY))
2288 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2289 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2294 /***********************************************************************
2295 * MENU_ShowSubPopup
2297 * Display the sub-menu of the selected item of this menu.
2298 * Return the handle of the submenu, or hmenu if no submenu to display.
2300 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2301 BOOL selectFirst, UINT wFlags )
2303 RECT rect;
2304 POPUPMENU *menu;
2305 MENUITEM *item;
2306 HDC hdc;
2308 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2310 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2312 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2314 item = &menu->items[menu->FocusedItem];
2315 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2316 return hmenu;
2318 /* message must be sent before using item,
2319 because nearly everything may be changed by the application ! */
2321 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2322 if (!(wFlags & TPM_NONOTIFY))
2323 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2324 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2326 item = &menu->items[menu->FocusedItem];
2327 rect = item->rect;
2329 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2330 if (!(item->fState & MF_HILITE))
2332 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2333 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2335 SelectObject( hdc, get_menu_font(FALSE));
2337 item->fState |= MF_HILITE;
2338 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2339 ReleaseDC( menu->hWnd, hdc );
2341 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2342 item->rect = rect;
2344 item->fState |= MF_MOUSESELECT;
2346 if (IS_SYSTEM_MENU(menu))
2348 MENU_InitSysMenuPopup(item->hSubMenu,
2349 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2350 GetClassLongW( menu->hWnd, GCL_STYLE));
2352 NC_GetSysPopupPos( menu->hWnd, &rect );
2353 rect.top = rect.bottom;
2354 rect.right = GetSystemMetrics(SM_CXSIZE);
2355 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2357 else
2359 GetWindowRect( menu->hWnd, &rect );
2360 if (menu->wFlags & MF_POPUP)
2362 RECT rc = item->rect;
2364 MENU_AdjustMenuItemRect(menu, &rc);
2366 /* The first item in the popup menu has to be at the
2367 same y position as the focused menu item */
2368 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2369 rect.top += rc.top - MENU_TOP_MARGIN;
2370 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2371 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2372 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2374 else
2376 rect.left += item->rect.left;
2377 rect.top += item->rect.bottom;
2378 rect.right = item->rect.right - item->rect.left;
2379 rect.bottom = item->rect.bottom - item->rect.top;
2383 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2384 rect.left, rect.top, rect.right, rect.bottom );
2385 if (selectFirst)
2386 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2387 return item->hSubMenu;
2392 /**********************************************************************
2393 * MENU_IsMenuActive
2395 HWND MENU_IsMenuActive(void)
2397 return top_popup;
2400 /**********************************************************************
2401 * MENU_EndMenu
2403 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2405 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2407 void MENU_EndMenu( HWND hwnd )
2409 POPUPMENU *menu;
2410 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2411 if (menu && hwnd == menu->hwndOwner) EndMenu();
2414 /***********************************************************************
2415 * MENU_PtMenu
2417 * Walks menu chain trying to find a menu pt maps to.
2419 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2421 POPUPMENU *menu = MENU_GetMenu( hMenu );
2422 UINT item = menu->FocusedItem;
2423 HMENU ret;
2425 /* try subpopup first (if any) */
2426 ret = (item != NO_SELECTED_ITEM &&
2427 (menu->items[item].fType & MF_POPUP) &&
2428 (menu->items[item].fState & MF_MOUSESELECT))
2429 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2431 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2433 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2434 if( menu->wFlags & MF_POPUP )
2436 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2438 else if (ht == HTSYSMENU)
2439 ret = get_win_sys_menu( menu->hWnd );
2440 else if (ht == HTMENU)
2441 ret = GetMenu( menu->hWnd );
2443 return ret;
2446 /***********************************************************************
2447 * MENU_ExecFocusedItem
2449 * Execute a menu item (for instance when user pressed Enter).
2450 * Return the wID of the executed item. Otherwise, -1 indicating
2451 * that no menu item was executed, -2 if a popup is shown;
2452 * Have to receive the flags for the TrackPopupMenu options to avoid
2453 * sending unwanted message.
2456 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2458 MENUITEM *item;
2459 POPUPMENU *menu = MENU_GetMenu( hMenu );
2461 TRACE("%p hmenu=%p\n", pmt, hMenu);
2463 if (!menu || !menu->nItems ||
2464 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2466 item = &menu->items[menu->FocusedItem];
2468 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2470 if (!(item->fType & MF_POPUP))
2472 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2474 /* If TPM_RETURNCMD is set you return the id, but
2475 do not send a message to the owner */
2476 if(!(wFlags & TPM_RETURNCMD))
2478 if( menu->wFlags & MF_SYSMENU )
2479 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2480 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2481 else
2483 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2484 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2486 if (dwStyle & MNS_NOTIFYBYPOS)
2487 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2488 (LPARAM)hMenu);
2489 else
2490 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2493 return item->wID;
2496 else
2498 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2499 return -2;
2502 return -1;
2505 /***********************************************************************
2506 * MENU_SwitchTracking
2508 * Helper function for menu navigation routines.
2510 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2512 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2513 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2515 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2517 if( pmt->hTopMenu != hPtMenu &&
2518 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2520 /* both are top level menus (system and menu-bar) */
2521 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2522 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2523 pmt->hTopMenu = hPtMenu;
2525 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2526 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2530 /***********************************************************************
2531 * MENU_ButtonDown
2533 * Return TRUE if we can go on with menu tracking.
2535 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2537 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2539 if (hPtMenu)
2541 UINT id = 0;
2542 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2543 MENUITEM *item;
2545 if( IS_SYSTEM_MENU(ptmenu) )
2546 item = ptmenu->items;
2547 else
2548 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2550 if( item )
2552 if( ptmenu->FocusedItem != id )
2553 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2555 /* If the popup menu is not already "popped" */
2556 if(!(item->fState & MF_MOUSESELECT ))
2558 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2561 return TRUE;
2563 /* Else the click was on the menu bar, finish the tracking */
2565 return FALSE;
2568 /***********************************************************************
2569 * MENU_ButtonUp
2571 * Return the value of MENU_ExecFocusedItem if
2572 * the selected item was not a popup. Else open the popup.
2573 * A -1 return value indicates that we go on with menu tracking.
2576 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2578 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2580 if (hPtMenu)
2582 UINT id = 0;
2583 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2584 MENUITEM *item;
2586 if( IS_SYSTEM_MENU(ptmenu) )
2587 item = ptmenu->items;
2588 else
2589 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2591 if( item && (ptmenu->FocusedItem == id ))
2593 debug_print_menuitem ("FocusedItem: ", item, "");
2595 if( !(item->fType & MF_POPUP) )
2597 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2598 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2599 return executedMenuId;
2602 /* If we are dealing with the top-level menu */
2603 /* and this is a click on an already "popped" item: */
2604 /* Stop the menu tracking and close the opened submenus */
2605 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2606 return 0;
2608 ptmenu->bTimeToHide = TRUE;
2610 return -1;
2614 /***********************************************************************
2615 * MENU_MouseMove
2617 * Return TRUE if we can go on with menu tracking.
2619 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2621 UINT id = NO_SELECTED_ITEM;
2622 POPUPMENU *ptmenu = NULL;
2624 if( hPtMenu )
2626 ptmenu = MENU_GetMenu( hPtMenu );
2627 if( IS_SYSTEM_MENU(ptmenu) )
2628 id = 0;
2629 else
2630 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2633 if( id == NO_SELECTED_ITEM )
2635 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2636 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2639 else if( ptmenu->FocusedItem != id )
2641 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2642 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2644 return TRUE;
2648 /***********************************************************************
2649 * MENU_DoNextMenu
2651 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2653 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2655 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2656 BOOL atEnd = FALSE;
2658 /* When skipping left, we need to do something special after the
2659 first menu. */
2660 if (vk == VK_LEFT && menu->FocusedItem == 0)
2662 atEnd = TRUE;
2664 /* When skipping right, for the non-system menu, we need to
2665 handle the last non-special menu item (ie skip any window
2666 icons such as MDI maximize, restore or close) */
2667 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2669 UINT i = menu->FocusedItem + 1;
2670 while (i < menu->nItems) {
2671 if ((menu->items[i].wID >= SC_SIZE &&
2672 menu->items[i].wID <= SC_RESTORE)) {
2673 i++;
2674 } else break;
2676 if (i == menu->nItems) {
2677 atEnd = TRUE;
2680 /* When skipping right, we need to cater for the system menu */
2681 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2683 if (menu->FocusedItem == (menu->nItems - 1)) {
2684 atEnd = TRUE;
2688 if( atEnd )
2690 MDINEXTMENU next_menu;
2691 HMENU hNewMenu;
2692 HWND hNewWnd;
2693 UINT id = 0;
2695 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2696 next_menu.hmenuNext = 0;
2697 next_menu.hwndNext = 0;
2698 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2700 TRACE("%p [%p] -> %p [%p]\n",
2701 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2703 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2705 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2706 hNewWnd = pmt->hOwnerWnd;
2707 if( IS_SYSTEM_MENU(menu) )
2709 /* switch to the menu bar */
2711 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2713 if( vk == VK_LEFT )
2715 menu = MENU_GetMenu( hNewMenu );
2716 id = menu->nItems - 1;
2718 /* Skip backwards over any system predefined icons,
2719 eg. MDI close, restore etc icons */
2720 while ((id > 0) &&
2721 (menu->items[id].wID >= SC_SIZE &&
2722 menu->items[id].wID <= SC_RESTORE)) id--;
2725 else if (style & WS_SYSMENU )
2727 /* switch to the system menu */
2728 hNewMenu = get_win_sys_menu( hNewWnd );
2730 else return FALSE;
2732 else /* application returned a new menu to switch to */
2734 hNewMenu = next_menu.hmenuNext;
2735 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2737 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2739 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2741 if (style & WS_SYSMENU &&
2742 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2744 /* get the real system menu */
2745 hNewMenu = get_win_sys_menu(hNewWnd);
2747 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2749 /* FIXME: Not sure what to do here;
2750 * perhaps try to track hNewMenu as a popup? */
2752 TRACE(" -- got confused.\n");
2753 return FALSE;
2756 else return FALSE;
2759 if( hNewMenu != pmt->hTopMenu )
2761 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2762 FALSE, 0 );
2763 if( pmt->hCurrentMenu != pmt->hTopMenu )
2764 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2767 if( hNewWnd != pmt->hOwnerWnd )
2769 pmt->hOwnerWnd = hNewWnd;
2770 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2773 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2774 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2776 return TRUE;
2778 return FALSE;
2781 /***********************************************************************
2782 * MENU_SuspendPopup
2784 * The idea is not to show the popup if the next input message is
2785 * going to hide it anyway.
2787 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2789 MSG msg;
2791 msg.hwnd = pmt->hOwnerWnd;
2793 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2794 pmt->trackFlags |= TF_SKIPREMOVE;
2796 switch( uMsg )
2798 case WM_KEYDOWN:
2799 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2800 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2802 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2803 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2804 if( msg.message == WM_KEYDOWN &&
2805 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2807 pmt->trackFlags |= TF_SUSPENDPOPUP;
2808 return TRUE;
2811 break;
2814 /* failures go through this */
2815 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2816 return FALSE;
2819 /***********************************************************************
2820 * MENU_KeyEscape
2822 * Handle a VK_ESCAPE key event in a menu.
2824 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2826 BOOL bEndMenu = TRUE;
2828 if (pmt->hCurrentMenu != pmt->hTopMenu)
2830 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2832 if (menu->wFlags & MF_POPUP)
2834 HMENU hmenutmp, hmenuprev;
2836 hmenuprev = hmenutmp = pmt->hTopMenu;
2838 /* close topmost popup */
2839 while (hmenutmp != pmt->hCurrentMenu)
2841 hmenuprev = hmenutmp;
2842 hmenutmp = MENU_GetSubPopup( hmenuprev );
2845 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2846 pmt->hCurrentMenu = hmenuprev;
2847 bEndMenu = FALSE;
2851 return bEndMenu;
2854 /***********************************************************************
2855 * MENU_KeyLeft
2857 * Handle a VK_LEFT key event in a menu.
2859 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2861 POPUPMENU *menu;
2862 HMENU hmenutmp, hmenuprev;
2863 UINT prevcol;
2865 hmenuprev = hmenutmp = pmt->hTopMenu;
2866 menu = MENU_GetMenu( hmenutmp );
2868 /* Try to move 1 column left (if possible) */
2869 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2870 NO_SELECTED_ITEM ) {
2872 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2873 prevcol, TRUE, 0 );
2874 return;
2877 /* close topmost popup */
2878 while (hmenutmp != pmt->hCurrentMenu)
2880 hmenuprev = hmenutmp;
2881 hmenutmp = MENU_GetSubPopup( hmenuprev );
2884 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2885 pmt->hCurrentMenu = hmenuprev;
2887 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2889 /* move menu bar selection if no more popups are left */
2891 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2892 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2894 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2896 /* A sublevel menu was displayed - display the next one
2897 * unless there is another displacement coming up */
2899 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2900 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2901 pmt->hTopMenu, TRUE, wFlags);
2907 /***********************************************************************
2908 * MENU_KeyRight
2910 * Handle a VK_RIGHT key event in a menu.
2912 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2914 HMENU hmenutmp;
2915 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2916 UINT nextcol;
2918 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2919 pmt->hCurrentMenu,
2920 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2921 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2923 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2925 /* If already displaying a popup, try to display sub-popup */
2927 hmenutmp = pmt->hCurrentMenu;
2928 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2930 /* if subpopup was displayed then we are done */
2931 if (hmenutmp != pmt->hCurrentMenu) return;
2934 /* Check to see if there's another column */
2935 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2936 NO_SELECTED_ITEM ) {
2937 TRACE("Going to %d.\n", nextcol );
2938 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2939 nextcol, TRUE, 0 );
2940 return;
2943 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2945 if( pmt->hCurrentMenu != pmt->hTopMenu )
2947 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2948 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2949 } else hmenutmp = 0;
2951 /* try to move to the next item */
2952 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2953 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2955 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2956 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2957 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2958 pmt->hTopMenu, TRUE, wFlags);
2962 static void CALLBACK release_capture( BOOL __normal )
2964 set_capture_window( 0, GUI_INMENUMODE, NULL );
2967 /***********************************************************************
2968 * MENU_TrackMenu
2970 * Menu tracking code.
2972 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2973 HWND hwnd, const RECT *lprect )
2975 MSG msg;
2976 POPUPMENU *menu;
2977 BOOL fRemove;
2978 INT executedMenuId = -1;
2979 MTRACKER mt;
2980 BOOL enterIdleSent = FALSE;
2981 HWND capture_win;
2983 mt.trackFlags = 0;
2984 mt.hCurrentMenu = hmenu;
2985 mt.hTopMenu = hmenu;
2986 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2987 mt.pt.x = x;
2988 mt.pt.y = y;
2990 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2991 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2993 fEndMenu = FALSE;
2994 if (!(menu = MENU_GetMenu( hmenu )))
2996 WARN("Invalid menu handle %p\n", hmenu);
2997 SetLastError(ERROR_INVALID_MENU_HANDLE);
2998 return FALSE;
3001 if (wFlags & TPM_BUTTONDOWN)
3003 /* Get the result in order to start the tracking or not */
3004 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3005 fEndMenu = !fRemove;
3008 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3010 /* owner may not be visible when tracking a popup, so use the menu itself */
3011 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3012 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3014 __TRY while (!fEndMenu)
3016 menu = MENU_GetMenu( mt.hCurrentMenu );
3017 if (!menu) /* sometimes happens if I do a window manager close */
3018 break;
3020 /* we have to keep the message in the queue until it's
3021 * clear that menu loop is not over yet. */
3023 for (;;)
3025 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3027 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3028 /* remove the message from the queue */
3029 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3031 else
3033 if (!enterIdleSent)
3035 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3036 enterIdleSent = TRUE;
3037 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3039 WaitMessage();
3043 /* check if EndMenu() tried to cancel us, by posting this message */
3044 if(msg.message == WM_CANCELMODE)
3046 /* we are now out of the loop */
3047 fEndMenu = TRUE;
3049 /* remove the message from the queue */
3050 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3052 /* break out of internal loop, ala ESCAPE */
3053 break;
3056 TranslateMessage( &msg );
3057 mt.pt = msg.pt;
3059 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3060 enterIdleSent=FALSE;
3062 fRemove = FALSE;
3063 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3066 * Use the mouse coordinates in lParam instead of those in the MSG
3067 * struct to properly handle synthetic messages. They are already
3068 * in screen coordinates.
3070 mt.pt.x = (short)LOWORD(msg.lParam);
3071 mt.pt.y = (short)HIWORD(msg.lParam);
3073 /* Find a menu for this mouse event */
3074 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3076 switch(msg.message)
3078 /* no WM_NC... messages in captured state */
3080 case WM_RBUTTONDBLCLK:
3081 case WM_RBUTTONDOWN:
3082 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3083 /* fall through */
3084 case WM_LBUTTONDBLCLK:
3085 case WM_LBUTTONDOWN:
3086 /* If the message belongs to the menu, removes it from the queue */
3087 /* Else, end menu tracking */
3088 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3089 fEndMenu = !fRemove;
3090 break;
3092 case WM_RBUTTONUP:
3093 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3094 /* fall through */
3095 case WM_LBUTTONUP:
3096 /* Check if a menu was selected by the mouse */
3097 if (hmenu)
3099 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3100 TRACE("executedMenuId %d\n", executedMenuId);
3102 /* End the loop if executedMenuId is an item ID */
3103 /* or if the job was done (executedMenuId = 0). */
3104 fEndMenu = fRemove = (executedMenuId != -1);
3106 /* No menu was selected by the mouse */
3107 /* if the function was called by TrackPopupMenu, continue
3108 with the menu tracking. If not, stop it */
3109 else
3110 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3112 break;
3114 case WM_MOUSEMOVE:
3115 /* the selected menu item must be changed every time */
3116 /* the mouse moves. */
3118 if (hmenu)
3119 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3121 } /* switch(msg.message) - mouse */
3123 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3125 fRemove = TRUE; /* Keyboard messages are always removed */
3126 switch(msg.message)
3128 case WM_KEYDOWN:
3129 case WM_SYSKEYDOWN:
3130 switch(msg.wParam)
3132 case VK_MENU:
3133 fEndMenu = TRUE;
3134 break;
3136 case VK_HOME:
3137 case VK_END:
3138 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3139 NO_SELECTED_ITEM, FALSE, 0 );
3140 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3141 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3142 break;
3144 case VK_UP:
3145 case VK_DOWN: /* If on menu bar, pull-down the menu */
3147 menu = MENU_GetMenu( mt.hCurrentMenu );
3148 if (!(menu->wFlags & MF_POPUP))
3149 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3150 else /* otherwise try to move selection */
3151 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3152 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3153 break;
3155 case VK_LEFT:
3156 MENU_KeyLeft( &mt, wFlags );
3157 break;
3159 case VK_RIGHT:
3160 MENU_KeyRight( &mt, wFlags );
3161 break;
3163 case VK_ESCAPE:
3164 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3165 break;
3167 case VK_F1:
3169 HELPINFO hi;
3170 hi.cbSize = sizeof(HELPINFO);
3171 hi.iContextType = HELPINFO_MENUITEM;
3172 if (menu->FocusedItem == NO_SELECTED_ITEM)
3173 hi.iCtrlId = 0;
3174 else
3175 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3176 hi.hItemHandle = hmenu;
3177 hi.dwContextId = menu->dwContextHelpID;
3178 hi.MousePos = msg.pt;
3179 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3180 break;
3183 default:
3184 break;
3186 break; /* WM_KEYDOWN */
3188 case WM_CHAR:
3189 case WM_SYSCHAR:
3191 UINT pos;
3193 if (msg.wParam == '\r' || msg.wParam == ' ')
3195 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3196 fEndMenu = (executedMenuId != -2);
3198 break;
3201 /* Hack to avoid control chars. */
3202 /* We will find a better way real soon... */
3203 if (msg.wParam < 32) break;
3205 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3206 LOWORD(msg.wParam), FALSE );
3207 if (pos == (UINT)-2) fEndMenu = TRUE;
3208 else if (pos == (UINT)-1) MessageBeep(0);
3209 else
3211 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3212 TRUE, 0 );
3213 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3214 fEndMenu = (executedMenuId != -2);
3217 break;
3218 } /* switch(msg.message) - kbd */
3220 else
3222 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3223 DispatchMessageW( &msg );
3224 continue;
3227 if (!fEndMenu) fRemove = TRUE;
3229 /* finally remove message from the queue */
3231 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3232 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3233 else mt.trackFlags &= ~TF_SKIPREMOVE;
3235 __FINALLY( release_capture )
3237 /* If dropdown is still painted and the close box is clicked on
3238 then the menu will be destroyed as part of the DispatchMessage above.
3239 This will then invalidate the menu handle in mt.hTopMenu. We should
3240 check for this first. */
3241 if( IsMenu( mt.hTopMenu ) )
3243 menu = MENU_GetMenu( mt.hTopMenu );
3245 if( IsWindow( mt.hOwnerWnd ) )
3247 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3249 if (menu && (menu->wFlags & MF_POPUP))
3251 DestroyWindow( menu->hWnd );
3252 menu->hWnd = 0;
3254 if (!(wFlags & TPM_NONOTIFY))
3255 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3256 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3258 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3259 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3262 /* Reset the variable for hiding menu */
3263 if( menu ) menu->bTimeToHide = FALSE;
3266 /* The return value is only used by TrackPopupMenu */
3267 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3268 if (executedMenuId == -1) executedMenuId = 0;
3269 return executedMenuId;
3272 /***********************************************************************
3273 * MENU_InitTracking
3275 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3277 POPUPMENU *menu;
3279 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3281 HideCaret(0);
3283 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3284 if (!(wFlags & TPM_NONOTIFY))
3285 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3287 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3289 if (!(wFlags & TPM_NONOTIFY))
3291 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3292 /* If an app changed/recreated menu bar entries in WM_INITMENU
3293 * menu sizes will be recalculated once the menu created/shown.
3297 /* This makes the menus of applications built with Delphi work.
3298 * It also enables menus to be displayed in more than one window,
3299 * but there are some bugs left that need to be fixed in this case.
3301 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3303 return TRUE;
3305 /***********************************************************************
3306 * MENU_ExitTracking
3308 static BOOL MENU_ExitTracking(HWND hWnd)
3310 TRACE("hwnd=%p\n", hWnd);
3312 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3313 ShowCaret(0);
3314 top_popup = 0;
3315 top_popup_hmenu = NULL;
3316 return TRUE;
3319 /***********************************************************************
3320 * MENU_TrackMouseMenuBar
3322 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3324 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3326 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3327 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3329 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3331 if (IsMenu(hMenu))
3333 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3334 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3335 MENU_ExitTracking(hWnd);
3340 /***********************************************************************
3341 * MENU_TrackKbdMenuBar
3343 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3345 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3347 UINT uItem = NO_SELECTED_ITEM;
3348 HMENU hTrackMenu;
3349 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3351 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3353 /* find window that has a menu */
3355 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3356 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3358 /* check if we have to track a system menu */
3360 hTrackMenu = GetMenu( hwnd );
3361 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3363 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3364 hTrackMenu = get_win_sys_menu( hwnd );
3365 uItem = 0;
3366 wParam |= HTSYSMENU; /* prevent item lookup */
3369 if (!IsMenu( hTrackMenu )) return;
3371 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3373 if( wChar && wChar != ' ' )
3375 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3376 if ( uItem >= (UINT)(-2) )
3378 if( uItem == (UINT)(-1) ) MessageBeep(0);
3379 /* schedule end of menu tracking */
3380 wFlags |= TF_ENDMENU;
3381 goto track_menu;
3385 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3387 if (!(wParam & HTSYSMENU) || wChar == ' ')
3389 if( uItem == NO_SELECTED_ITEM )
3390 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3391 else
3392 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3395 track_menu:
3396 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3397 MENU_ExitTracking( hwnd );
3400 /**********************************************************************
3401 * TrackPopupMenuEx (USER32.@)
3403 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3404 HWND hWnd, LPTPMPARAMS lpTpm )
3406 BOOL ret = FALSE;
3408 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3409 hMenu, wFlags, x, y, hWnd, lpTpm,
3410 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3412 /* Parameter check */
3413 /* FIXME: this check is performed several times, here and in the called
3414 functions. That could be optimized */
3415 if (!MENU_GetMenu( hMenu ))
3417 SetLastError( ERROR_INVALID_MENU_HANDLE );
3418 return FALSE;
3421 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3423 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3424 if (!(wFlags & TPM_NONOTIFY))
3425 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3427 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3428 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3429 lpTpm ? &lpTpm->rcExclude : NULL );
3430 MENU_ExitTracking(hWnd);
3432 return ret;
3435 /**********************************************************************
3436 * TrackPopupMenu (USER32.@)
3438 * Like the win32 API, the function return the command ID only if the
3439 * flag TPM_RETURNCMD is on.
3442 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3443 INT nReserved, HWND hWnd, const RECT *lpRect )
3445 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3448 /***********************************************************************
3449 * PopupMenuWndProc
3451 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3453 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3455 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3457 switch(message)
3459 case WM_CREATE:
3461 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3462 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3463 return 0;
3466 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3467 return MA_NOACTIVATE;
3469 case WM_PAINT:
3471 PAINTSTRUCT ps;
3472 BeginPaint( hwnd, &ps );
3473 MENU_DrawPopupMenu( hwnd, ps.hdc,
3474 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3475 EndPaint( hwnd, &ps );
3476 return 0;
3479 case WM_PRINTCLIENT:
3481 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3482 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3483 return 0;
3486 case WM_ERASEBKGND:
3487 return 1;
3489 case WM_DESTROY:
3490 /* zero out global pointer in case resident popup window was destroyed. */
3491 if (hwnd == top_popup) {
3492 top_popup = 0;
3493 top_popup_hmenu = NULL;
3495 break;
3497 case WM_SHOWWINDOW:
3499 if( wParam )
3501 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3503 else
3504 SetWindowLongPtrW( hwnd, 0, 0 );
3505 break;
3507 case MM_SETMENUHANDLE:
3508 SetWindowLongPtrW( hwnd, 0, wParam );
3509 break;
3511 case MM_GETMENUHANDLE:
3512 case MN_GETHMENU:
3513 return GetWindowLongPtrW( hwnd, 0 );
3515 default:
3516 return DefWindowProcW( hwnd, message, wParam, lParam );
3518 return 0;
3522 /***********************************************************************
3523 * MENU_GetMenuBarHeight
3525 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3527 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3528 INT orgX, INT orgY )
3530 HDC hdc;
3531 RECT rectBar;
3532 LPPOPUPMENU lppop;
3534 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3536 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3538 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3539 SelectObject( hdc, get_menu_font(FALSE));
3540 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3541 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3542 ReleaseDC( hwnd, hdc );
3543 return lppop->Height;
3547 /*******************************************************************
3548 * ChangeMenuA (USER32.@)
3550 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3551 UINT id, UINT flags )
3553 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3554 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3555 id, data );
3556 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3557 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3558 id, data );
3559 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3560 flags & MF_BYPOSITION ? pos : id,
3561 flags & ~MF_REMOVE );
3562 /* Default: MF_INSERT */
3563 return InsertMenuA( hMenu, pos, flags, id, data );
3567 /*******************************************************************
3568 * ChangeMenuW (USER32.@)
3570 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3571 UINT id, UINT flags )
3573 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3574 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3575 id, data );
3576 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3577 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3578 id, data );
3579 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3580 flags & MF_BYPOSITION ? pos : id,
3581 flags & ~MF_REMOVE );
3582 /* Default: MF_INSERT */
3583 return InsertMenuW( hMenu, pos, flags, id, data );
3587 /*******************************************************************
3588 * CheckMenuItem (USER32.@)
3590 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3592 MENUITEM *item;
3593 DWORD ret;
3595 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3596 ret = item->fState & MF_CHECKED;
3597 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3598 else item->fState &= ~MF_CHECKED;
3599 return ret;
3603 /**********************************************************************
3604 * EnableMenuItem (USER32.@)
3606 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3608 UINT oldflags;
3609 MENUITEM *item;
3610 POPUPMENU *menu;
3612 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3614 /* Get the Popupmenu to access the owner menu */
3615 if (!(menu = MENU_GetMenu(hMenu)))
3616 return (UINT)-1;
3618 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3619 return (UINT)-1;
3621 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3622 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3624 /* If the close item in the system menu change update the close button */
3625 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3627 if (menu->hSysMenuOwner != 0)
3629 RECT rc;
3630 POPUPMENU* parentMenu;
3632 /* Get the parent menu to access*/
3633 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3634 return (UINT)-1;
3636 /* Refresh the frame to reflect the change */
3637 GetWindowRect(parentMenu->hWnd, &rc);
3638 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3639 rc.bottom = 0;
3640 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3644 return oldflags;
3648 /*******************************************************************
3649 * GetMenuStringA (USER32.@)
3651 INT WINAPI GetMenuStringA(
3652 HMENU hMenu, /* [in] menuhandle */
3653 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3654 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3655 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3656 UINT wFlags /* [in] MF_ flags */
3658 MENUITEM *item;
3660 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3661 if (str && nMaxSiz) str[0] = '\0';
3662 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3663 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3664 return 0;
3666 if (!item->text) return 0;
3667 if (!str || !nMaxSiz) return strlenW(item->text);
3668 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3669 str[nMaxSiz-1] = 0;
3670 TRACE("returning %s\n", debugstr_a(str));
3671 return strlen(str);
3675 /*******************************************************************
3676 * GetMenuStringW (USER32.@)
3678 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3679 LPWSTR str, INT nMaxSiz, UINT wFlags )
3681 MENUITEM *item;
3683 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3684 if (str && nMaxSiz) str[0] = '\0';
3685 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3686 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3687 return 0;
3689 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3690 if( !(item->text)) {
3691 str[0] = 0;
3692 return 0;
3694 lstrcpynW( str, item->text, nMaxSiz );
3695 TRACE("returning %s\n", debugstr_w(str));
3696 return strlenW(str);
3700 /**********************************************************************
3701 * HiliteMenuItem (USER32.@)
3703 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3704 UINT wHilite )
3706 LPPOPUPMENU menu;
3707 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3708 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3709 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3710 if (menu->FocusedItem == wItemID) return TRUE;
3711 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3712 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3713 return TRUE;
3717 /**********************************************************************
3718 * GetMenuState (USER32.@)
3720 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3722 MENUITEM *item;
3723 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3724 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3725 debug_print_menuitem (" item: ", item, "");
3726 if (item->fType & MF_POPUP)
3728 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3729 if (!menu) return -1;
3730 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3732 else
3734 /* We used to (from way back then) mask the result to 0xff. */
3735 /* I don't know why and it seems wrong as the documented */
3736 /* return flag MF_SEPARATOR is outside that mask. */
3737 return (item->fType | item->fState);
3742 /**********************************************************************
3743 * GetMenuItemCount (USER32.@)
3745 INT WINAPI GetMenuItemCount( HMENU hMenu )
3747 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3748 if (!menu) return -1;
3749 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3750 return menu->nItems;
3754 /**********************************************************************
3755 * GetMenuItemID (USER32.@)
3757 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3759 MENUITEM * lpmi;
3761 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3762 if (lpmi->fType & MF_POPUP) return -1;
3763 return lpmi->wID;
3768 /**********************************************************************
3769 * MENU_mnu2mnuii
3771 * Uses flags, id and text ptr, passed by InsertMenu() and
3772 * ModifyMenu() to setup a MenuItemInfo structure.
3774 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3775 LPMENUITEMINFOW pmii)
3777 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3778 pmii->cbSize = sizeof( MENUITEMINFOW);
3779 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3780 /* setting bitmap clears text and vice versa */
3781 if( IS_STRING_ITEM(flags)) {
3782 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3783 if( !str)
3784 flags |= MF_SEPARATOR;
3785 /* Item beginning with a backspace is a help item */
3786 /* FIXME: wrong place, this is only true in win16 */
3787 else if( *str == '\b') {
3788 flags |= MF_HELP;
3789 str++;
3791 pmii->dwTypeData = (LPWSTR)str;
3792 } else if( flags & MFT_BITMAP){
3793 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3794 pmii->hbmpItem = HBITMAP_32(LOWORD(str));
3796 if( flags & MF_OWNERDRAW){
3797 pmii->fMask |= MIIM_DATA;
3798 pmii->dwItemData = (ULONG_PTR) str;
3800 if( flags & MF_POPUP) {
3801 pmii->fMask |= MIIM_SUBMENU;
3802 pmii->hSubMenu = (HMENU)id;
3804 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3805 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3806 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3807 pmii->wID = (UINT)id;
3811 /*******************************************************************
3812 * InsertMenuW (USER32.@)
3814 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3815 UINT_PTR id, LPCWSTR str )
3817 MENUITEM *item;
3818 MENUITEMINFOW mii;
3820 if (IS_STRING_ITEM(flags) && str)
3821 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3822 hMenu, pos, flags, id, debugstr_w(str) );
3823 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3824 hMenu, pos, flags, id, str );
3826 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3827 MENU_mnu2mnuii( flags, id, str, &mii);
3828 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3830 RemoveMenu( hMenu, pos, flags );
3831 return FALSE;
3834 item->hCheckBit = item->hUnCheckBit = 0;
3835 return TRUE;
3839 /*******************************************************************
3840 * InsertMenuA (USER32.@)
3842 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3843 UINT_PTR id, LPCSTR str )
3845 BOOL ret = FALSE;
3847 if (IS_STRING_ITEM(flags) && str)
3849 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3850 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3851 if (newstr)
3853 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3854 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3855 HeapFree( GetProcessHeap(), 0, newstr );
3857 return ret;
3859 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3863 /*******************************************************************
3864 * AppendMenuA (USER32.@)
3866 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3867 UINT_PTR id, LPCSTR data )
3869 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3873 /*******************************************************************
3874 * AppendMenuW (USER32.@)
3876 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3877 UINT_PTR id, LPCWSTR data )
3879 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3883 /**********************************************************************
3884 * RemoveMenu (USER32.@)
3886 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3888 LPPOPUPMENU menu;
3889 MENUITEM *item;
3891 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3892 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3893 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3895 /* Remove item */
3897 MENU_FreeItemData( item );
3899 if (--menu->nItems == 0)
3901 HeapFree( GetProcessHeap(), 0, menu->items );
3902 menu->items = NULL;
3904 else
3906 while(nPos < menu->nItems)
3908 *item = *(item+1);
3909 item++;
3910 nPos++;
3912 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3913 menu->nItems * sizeof(MENUITEM) );
3915 return TRUE;
3919 /**********************************************************************
3920 * DeleteMenu (USER32.@)
3922 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3924 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3925 if (!item) return FALSE;
3926 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3927 /* nPos is now the position of the item */
3928 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3929 return TRUE;
3933 /*******************************************************************
3934 * ModifyMenuW (USER32.@)
3936 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3937 UINT_PTR id, LPCWSTR str )
3939 MENUITEM *item;
3940 MENUITEMINFOW mii;
3942 if (IS_STRING_ITEM(flags))
3943 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3944 else
3945 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3947 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3948 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3949 MENU_mnu2mnuii( flags, id, str, &mii);
3950 return SetMenuItemInfo_common( item, &mii, TRUE);
3954 /*******************************************************************
3955 * ModifyMenuA (USER32.@)
3957 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3958 UINT_PTR id, LPCSTR str )
3960 BOOL ret = FALSE;
3962 if (IS_STRING_ITEM(flags) && str)
3964 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3965 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3966 if (newstr)
3968 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3969 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3970 HeapFree( GetProcessHeap(), 0, newstr );
3972 return ret;
3974 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3978 /**********************************************************************
3979 * CreatePopupMenu (USER32.@)
3981 HMENU WINAPI CreatePopupMenu(void)
3983 HMENU hmenu;
3984 POPUPMENU *menu;
3986 if (!(hmenu = CreateMenu())) return 0;
3987 menu = MENU_GetMenu( hmenu );
3988 menu->wFlags |= MF_POPUP;
3989 menu->bTimeToHide = FALSE;
3990 return hmenu;
3994 /**********************************************************************
3995 * GetMenuCheckMarkDimensions (USER.417)
3996 * GetMenuCheckMarkDimensions (USER32.@)
3998 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4000 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4004 /**********************************************************************
4005 * SetMenuItemBitmaps (USER32.@)
4007 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4008 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4010 MENUITEM *item;
4012 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4014 if (!hNewCheck && !hNewUnCheck)
4016 item->fState &= ~MF_USECHECKBITMAPS;
4018 else /* Install new bitmaps */
4020 item->hCheckBit = hNewCheck;
4021 item->hUnCheckBit = hNewUnCheck;
4022 item->fState |= MF_USECHECKBITMAPS;
4024 return TRUE;
4028 /**********************************************************************
4029 * CreateMenu (USER32.@)
4031 HMENU WINAPI CreateMenu(void)
4033 HMENU hMenu;
4034 LPPOPUPMENU menu;
4036 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4037 menu->FocusedItem = NO_SELECTED_ITEM;
4038 menu->bTimeToHide = FALSE;
4040 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4042 TRACE("return %p\n", hMenu );
4044 return hMenu;
4048 /**********************************************************************
4049 * DestroyMenu (USER32.@)
4051 BOOL WINAPI DestroyMenu( HMENU hMenu )
4053 LPPOPUPMENU lppop;
4055 TRACE("(%p)\n", hMenu);
4057 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4058 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4060 /* DestroyMenu should not destroy system menu popup owner */
4061 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4063 DestroyWindow( lppop->hWnd );
4064 lppop->hWnd = 0;
4067 if (lppop->items) /* recursively destroy submenus */
4069 int i;
4070 MENUITEM *item = lppop->items;
4071 for (i = lppop->nItems; i > 0; i--, item++)
4073 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4074 MENU_FreeItemData( item );
4076 HeapFree( GetProcessHeap(), 0, lppop->items );
4078 HeapFree( GetProcessHeap(), 0, lppop );
4079 return TRUE;
4083 /**********************************************************************
4084 * GetSystemMenu (USER32.@)
4086 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4088 WND *wndPtr = WIN_GetPtr( hWnd );
4089 HMENU retvalue = 0;
4091 if (wndPtr == WND_DESKTOP) return 0;
4092 if (wndPtr == WND_OTHER_PROCESS)
4094 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4096 else if (wndPtr)
4098 if (wndPtr->hSysMenu && bRevert)
4100 DestroyMenu(wndPtr->hSysMenu);
4101 wndPtr->hSysMenu = 0;
4104 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4105 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4107 if( wndPtr->hSysMenu )
4109 POPUPMENU *menu;
4110 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4112 /* Store the dummy sysmenu handle to facilitate the refresh */
4113 /* of the close button if the SC_CLOSE item change */
4114 menu = MENU_GetMenu(retvalue);
4115 if ( menu )
4116 menu->hSysMenuOwner = wndPtr->hSysMenu;
4118 WIN_ReleasePtr( wndPtr );
4120 return bRevert ? 0 : retvalue;
4124 /*******************************************************************
4125 * SetSystemMenu (USER32.@)
4127 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4129 WND *wndPtr = WIN_GetPtr( hwnd );
4131 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4133 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4134 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4135 WIN_ReleasePtr( wndPtr );
4136 return TRUE;
4138 return FALSE;
4142 /**********************************************************************
4143 * GetMenu (USER32.@)
4145 HMENU WINAPI GetMenu( HWND hWnd )
4147 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4148 TRACE("for %p returning %p\n", hWnd, retvalue);
4149 return retvalue;
4152 /**********************************************************************
4153 * GetMenuBarInfo (USER32.@)
4155 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4157 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4158 return FALSE;
4161 /**********************************************************************
4162 * MENU_SetMenu
4164 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4165 * SetWindowPos call that would result if SetMenu were called directly.
4167 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4169 TRACE("(%p, %p);\n", hWnd, hMenu);
4171 if (hMenu && !IsMenu(hMenu))
4173 WARN("hMenu %p is not a menu handle\n", hMenu);
4174 return FALSE;
4176 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4177 return FALSE;
4179 hWnd = WIN_GetFullHandle( hWnd );
4180 if (GetCapture() == hWnd)
4181 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4183 if (hMenu != 0)
4185 LPPOPUPMENU lpmenu;
4187 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4189 lpmenu->hWnd = hWnd;
4190 lpmenu->Height = 0; /* Make sure we recalculate the size */
4192 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4193 return TRUE;
4197 /**********************************************************************
4198 * SetMenu (USER32.@)
4200 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4202 if(!MENU_SetMenu(hWnd, hMenu))
4203 return FALSE;
4205 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4206 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4207 return TRUE;
4211 /**********************************************************************
4212 * GetSubMenu (USER32.@)
4214 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4216 MENUITEM * lpmi;
4218 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4219 if (!(lpmi->fType & MF_POPUP)) return 0;
4220 return lpmi->hSubMenu;
4224 /**********************************************************************
4225 * DrawMenuBar (USER32.@)
4227 BOOL WINAPI DrawMenuBar( HWND hWnd )
4229 LPPOPUPMENU lppop;
4230 HMENU hMenu = GetMenu(hWnd);
4232 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4233 return FALSE;
4234 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4236 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4237 lppop->hwndOwner = hWnd;
4238 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4239 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4240 return TRUE;
4243 /***********************************************************************
4244 * DrawMenuBarTemp (USER32.@)
4246 * UNDOCUMENTED !!
4248 * called by W98SE desk.cpl Control Panel Applet
4250 * Not 100% sure about the param names, but close.
4252 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4254 LPPOPUPMENU lppop;
4255 UINT i,retvalue;
4256 HFONT hfontOld = 0;
4257 BOOL flat_menu = FALSE;
4259 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4261 if (!hMenu)
4262 hMenu = GetMenu(hwnd);
4264 if (!hFont)
4265 hFont = get_menu_font(FALSE);
4267 lppop = MENU_GetMenu( hMenu );
4268 if (lppop == NULL || lprect == NULL)
4270 retvalue = GetSystemMetrics(SM_CYMENU);
4271 goto END;
4274 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4276 hfontOld = SelectObject( hDC, hFont);
4278 if (lppop->Height == 0)
4279 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4281 lprect->bottom = lprect->top + lppop->Height;
4283 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4285 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4286 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4287 LineTo( hDC, lprect->right, lprect->bottom );
4289 if (lppop->nItems == 0)
4291 retvalue = GetSystemMetrics(SM_CYMENU);
4292 goto END;
4295 for (i = 0; i < lppop->nItems; i++)
4297 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4298 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4300 retvalue = lppop->Height;
4302 END:
4303 if (hfontOld) SelectObject (hDC, hfontOld);
4304 return retvalue;
4307 /***********************************************************************
4308 * EndMenu (USER.187)
4309 * EndMenu (USER32.@)
4311 BOOL WINAPI EndMenu(void)
4313 /* if we are in the menu code, and it is active */
4314 if (!fEndMenu && top_popup)
4316 /* terminate the menu handling code */
4317 fEndMenu = TRUE;
4319 /* needs to be posted to wakeup the internal menu handler */
4320 /* which will now terminate the menu, in the event that */
4321 /* the main window was minimized, or lost focus, so we */
4322 /* don't end up with an orphaned menu */
4323 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4325 return fEndMenu;
4329 /***********************************************************************
4330 * LookupMenuHandle (USER.217)
4332 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4334 HMENU hmenu32 = HMENU_32(hmenu);
4335 UINT id32 = id;
4336 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4337 else return HMENU_16(hmenu32);
4341 /**********************************************************************
4342 * LoadMenu (USER.150)
4344 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4346 HRSRC16 hRsrc;
4347 HGLOBAL16 handle;
4348 HMENU16 hMenu;
4350 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4351 if (!name) return 0;
4353 instance = GetExePtr( instance );
4354 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4355 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4356 hMenu = LoadMenuIndirect16(LockResource16(handle));
4357 FreeResource16( handle );
4358 return hMenu;
4362 /*****************************************************************
4363 * LoadMenuA (USER32.@)
4365 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4367 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4368 if (!hrsrc) return 0;
4369 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4373 /*****************************************************************
4374 * LoadMenuW (USER32.@)
4376 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4378 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4379 if (!hrsrc) return 0;
4380 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4384 /**********************************************************************
4385 * LoadMenuIndirect (USER.220)
4387 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4389 HMENU hMenu;
4390 WORD version, offset;
4391 LPCSTR p = template;
4393 TRACE("(%p)\n", template );
4394 version = GET_WORD(p);
4395 p += sizeof(WORD);
4396 if (version)
4398 WARN("version must be 0 for Win16\n" );
4399 return 0;
4401 offset = GET_WORD(p);
4402 p += sizeof(WORD) + offset;
4403 if (!(hMenu = CreateMenu())) return 0;
4404 if (!MENU_ParseResource( p, hMenu, FALSE ))
4406 DestroyMenu( hMenu );
4407 return 0;
4409 return HMENU_16(hMenu);
4413 /**********************************************************************
4414 * LoadMenuIndirectW (USER32.@)
4416 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4418 HMENU hMenu;
4419 WORD version, offset;
4420 LPCSTR p = template;
4422 version = GET_WORD(p);
4423 p += sizeof(WORD);
4424 TRACE("%p, ver %d\n", template, version );
4425 switch (version)
4427 case 0: /* standard format is version of 0 */
4428 offset = GET_WORD(p);
4429 p += sizeof(WORD) + offset;
4430 if (!(hMenu = CreateMenu())) return 0;
4431 if (!MENU_ParseResource( p, hMenu, TRUE ))
4433 DestroyMenu( hMenu );
4434 return 0;
4436 return hMenu;
4437 case 1: /* extended format is version of 1 */
4438 offset = GET_WORD(p);
4439 p += sizeof(WORD) + offset;
4440 if (!(hMenu = CreateMenu())) return 0;
4441 if (!MENUEX_ParseResource( p, hMenu))
4443 DestroyMenu( hMenu );
4444 return 0;
4446 return hMenu;
4447 default:
4448 ERR("version %d not supported.\n", version);
4449 return 0;
4454 /**********************************************************************
4455 * LoadMenuIndirectA (USER32.@)
4457 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4459 return LoadMenuIndirectW( template );
4463 /**********************************************************************
4464 * IsMenu (USER32.@)
4466 BOOL WINAPI IsMenu(HMENU hmenu)
4468 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4470 if (!menu)
4472 SetLastError(ERROR_INVALID_MENU_HANDLE);
4473 return FALSE;
4475 return TRUE;
4478 /**********************************************************************
4479 * GetMenuItemInfo_common
4482 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4483 LPMENUITEMINFOW lpmii, BOOL unicode)
4485 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4487 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4489 if (!menu) {
4490 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4491 return FALSE;
4494 if( lpmii->fMask & MIIM_TYPE) {
4495 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4496 WARN("invalid combination of fMask bits used\n");
4497 /* this does not happen on Win9x/ME */
4498 SetLastError( ERROR_INVALID_PARAMETER);
4499 return FALSE;
4501 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4502 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4503 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4504 if( lpmii->fType & MFT_BITMAP) {
4505 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4506 lpmii->cch = 0;
4507 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4508 /* this does not happen on Win9x/ME */
4509 lpmii->dwTypeData = 0;
4510 lpmii->cch = 0;
4514 /* copy the text string */
4515 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4516 if( !menu->text ) {
4517 if(lpmii->dwTypeData && lpmii->cch) {
4518 lpmii->cch = 0;
4519 if( unicode)
4520 *((WCHAR *)lpmii->dwTypeData) = 0;
4521 else
4522 *((CHAR *)lpmii->dwTypeData) = 0;
4524 } else {
4525 int len;
4526 if (unicode)
4528 len = strlenW(menu->text);
4529 if(lpmii->dwTypeData && lpmii->cch)
4530 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4532 else
4534 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4535 0, NULL, NULL ) - 1;
4536 if(lpmii->dwTypeData && lpmii->cch)
4537 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4538 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4539 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4541 /* if we've copied a substring we return its length */
4542 if(lpmii->dwTypeData && lpmii->cch)
4543 if (lpmii->cch <= len + 1)
4544 lpmii->cch--;
4545 else
4546 lpmii->cch = len;
4547 else {
4548 /* return length of string */
4549 /* not on Win9x/ME if fType & MFT_BITMAP */
4550 lpmii->cch = len;
4555 if (lpmii->fMask & MIIM_FTYPE)
4556 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4558 if (lpmii->fMask & MIIM_BITMAP)
4559 lpmii->hbmpItem = menu->hbmpItem;
4561 if (lpmii->fMask & MIIM_STATE)
4562 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4564 if (lpmii->fMask & MIIM_ID)
4565 lpmii->wID = menu->wID;
4567 if (lpmii->fMask & MIIM_SUBMENU)
4568 lpmii->hSubMenu = menu->hSubMenu;
4569 else {
4570 /* hSubMenu is always cleared
4571 * (not on Win9x/ME ) */
4572 lpmii->hSubMenu = 0;
4575 if (lpmii->fMask & MIIM_CHECKMARKS) {
4576 lpmii->hbmpChecked = menu->hCheckBit;
4577 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4579 if (lpmii->fMask & MIIM_DATA)
4580 lpmii->dwItemData = menu->dwItemData;
4582 return TRUE;
4585 /**********************************************************************
4586 * GetMenuItemInfoA (USER32.@)
4588 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4589 LPMENUITEMINFOA lpmii)
4591 BOOL ret;
4592 MENUITEMINFOA mii;
4593 if( lpmii->cbSize != sizeof( mii) &&
4594 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4595 SetLastError( ERROR_INVALID_PARAMETER);
4596 return FALSE;
4598 memcpy( &mii, lpmii, lpmii->cbSize);
4599 mii.cbSize = sizeof( mii);
4600 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4601 (LPMENUITEMINFOW)&mii, FALSE);
4602 mii.cbSize = lpmii->cbSize;
4603 memcpy( lpmii, &mii, mii.cbSize);
4604 return ret;
4607 /**********************************************************************
4608 * GetMenuItemInfoW (USER32.@)
4610 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4611 LPMENUITEMINFOW lpmii)
4613 BOOL ret;
4614 MENUITEMINFOW mii;
4615 if( lpmii->cbSize != sizeof( mii) &&
4616 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4617 SetLastError( ERROR_INVALID_PARAMETER);
4618 return FALSE;
4620 memcpy( &mii, lpmii, lpmii->cbSize);
4621 mii.cbSize = sizeof( mii);
4622 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4623 mii.cbSize = lpmii->cbSize;
4624 memcpy( lpmii, &mii, mii.cbSize);
4625 return ret;
4629 /* set a menu item text from a ASCII or Unicode string */
4630 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4632 if (!text)
4633 menu->text = NULL;
4634 else if (unicode)
4636 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4637 strcpyW( menu->text, text );
4639 else
4641 LPCSTR str = (LPCSTR)text;
4642 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4643 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4644 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4649 /**********************************************************************
4650 * MENU_depth
4652 * detect if there are loops in the menu tree (or the depth is too large)
4654 static int MENU_depth( POPUPMENU *pmenu, int depth)
4656 int i;
4657 MENUITEM *item;
4658 int subdepth;
4660 depth++;
4661 if( depth > MAXMENUDEPTH) return depth;
4662 item = pmenu->items;
4663 subdepth = depth;
4664 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4665 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4666 if( psubmenu){
4667 int bdepth = MENU_depth( psubmenu, depth);
4668 if( bdepth > subdepth) subdepth = bdepth;
4670 if( subdepth > MAXMENUDEPTH)
4671 TRACE("<- hmenu %p\n", item->hSubMenu);
4673 return subdepth;
4677 /**********************************************************************
4678 * SetMenuItemInfo_common
4680 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4681 * MIIM_BITMAP and MIIM_STRING flags instead.
4684 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4685 const MENUITEMINFOW *lpmii,
4686 BOOL unicode)
4688 if (!menu) return FALSE;
4690 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4692 if (lpmii->fMask & MIIM_FTYPE ) {
4693 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4694 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4696 if (lpmii->fMask & MIIM_STRING ) {
4697 /* free the string when used */
4698 HeapFree(GetProcessHeap(), 0, menu->text);
4699 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4702 if (lpmii->fMask & MIIM_STATE)
4703 /* Other menu items having MFS_DEFAULT are not converted
4704 to normal items */
4705 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4707 if (lpmii->fMask & MIIM_ID)
4708 menu->wID = lpmii->wID;
4710 if (lpmii->fMask & MIIM_SUBMENU) {
4711 menu->hSubMenu = lpmii->hSubMenu;
4712 if (menu->hSubMenu) {
4713 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4714 if (subMenu) {
4715 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4716 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4717 menu->hSubMenu = 0;
4718 return FALSE;
4720 subMenu->wFlags |= MF_POPUP;
4721 menu->fType |= MF_POPUP;
4722 } else {
4723 SetLastError( ERROR_INVALID_PARAMETER);
4724 return FALSE;
4727 else
4728 menu->fType &= ~MF_POPUP;
4731 if (lpmii->fMask & MIIM_CHECKMARKS)
4733 menu->hCheckBit = lpmii->hbmpChecked;
4734 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4736 if (lpmii->fMask & MIIM_DATA)
4737 menu->dwItemData = lpmii->dwItemData;
4739 if (lpmii->fMask & MIIM_BITMAP)
4740 menu->hbmpItem = lpmii->hbmpItem;
4742 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4743 menu->fType |= MFT_SEPARATOR;
4745 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4746 return TRUE;
4749 /**********************************************************************
4750 * MENU_NormalizeMenuItemInfoStruct
4752 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4753 * check, copy and extend the MENUITEMINFO struct from the version that the application
4754 * supplied to the version used by wine source. */
4755 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4756 MENUITEMINFOW *pmii_out )
4758 /* do we recognize the size? */
4759 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4760 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4761 SetLastError( ERROR_INVALID_PARAMETER);
4762 return FALSE;
4764 /* copy the fields that we have */
4765 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4766 /* if the hbmpItem member is missing then extend */
4767 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4768 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4769 pmii_out->hbmpItem = NULL;
4771 /* test for invalid bit combinations */
4772 if( (pmii_out->fMask & MIIM_TYPE &&
4773 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4774 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4775 WARN("invalid combination of fMask bits used\n");
4776 /* this does not happen on Win9x/ME */
4777 SetLastError( ERROR_INVALID_PARAMETER);
4778 return FALSE;
4780 /* convert old style (MIIM_TYPE) to the new */
4781 if( pmii_out->fMask & MIIM_TYPE){
4782 pmii_out->fMask |= MIIM_FTYPE;
4783 if( IS_STRING_ITEM(pmii_out->fType)){
4784 pmii_out->fMask |= MIIM_STRING;
4785 } else if( (pmii_out->fType) & MFT_BITMAP){
4786 pmii_out->fMask |= MIIM_BITMAP;
4787 pmii_out->hbmpItem = HBITMAP_32(LOWORD(pmii_out->dwTypeData));
4790 return TRUE;
4793 /**********************************************************************
4794 * SetMenuItemInfoA (USER32.@)
4796 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4797 const MENUITEMINFOA *lpmii)
4799 MENUITEMINFOW mii;
4801 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4803 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4805 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4806 &mii, FALSE);
4809 /**********************************************************************
4810 * SetMenuItemInfoW (USER32.@)
4812 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4813 const MENUITEMINFOW *lpmii)
4815 MENUITEMINFOW mii;
4817 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4819 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4820 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4821 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4824 /**********************************************************************
4825 * SetMenuDefaultItem (USER32.@)
4828 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4830 UINT i;
4831 POPUPMENU *menu;
4832 MENUITEM *item;
4834 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4836 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4838 /* reset all default-item flags */
4839 item = menu->items;
4840 for (i = 0; i < menu->nItems; i++, item++)
4842 item->fState &= ~MFS_DEFAULT;
4845 /* no default item */
4846 if ( -1 == uItem)
4848 return TRUE;
4851 item = menu->items;
4852 if ( bypos )
4854 if ( uItem >= menu->nItems ) return FALSE;
4855 item[uItem].fState |= MFS_DEFAULT;
4856 return TRUE;
4858 else
4860 for (i = 0; i < menu->nItems; i++, item++)
4862 if (item->wID == uItem)
4864 item->fState |= MFS_DEFAULT;
4865 return TRUE;
4870 return FALSE;
4873 /**********************************************************************
4874 * GetMenuDefaultItem (USER32.@)
4876 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4878 POPUPMENU *menu;
4879 MENUITEM * item;
4880 UINT i = 0;
4882 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4884 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4886 /* find default item */
4887 item = menu->items;
4889 /* empty menu */
4890 if (! item) return -1;
4892 while ( !( item->fState & MFS_DEFAULT ) )
4894 i++; item++;
4895 if (i >= menu->nItems ) return -1;
4898 /* default: don't return disabled items */
4899 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4901 /* search rekursiv when needed */
4902 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4904 UINT ret;
4905 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4906 if ( -1 != ret ) return ret;
4908 /* when item not found in submenu, return the popup item */
4910 return ( bypos ) ? i : item->wID;
4915 /**********************************************************************
4916 * InsertMenuItemA (USER32.@)
4918 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4919 const MENUITEMINFOA *lpmii)
4921 MENUITEM *item;
4922 MENUITEMINFOW mii;
4924 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4926 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4928 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4929 return SetMenuItemInfo_common(item, &mii, FALSE);
4933 /**********************************************************************
4934 * InsertMenuItemW (USER32.@)
4936 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4937 const MENUITEMINFOW *lpmii)
4939 MENUITEM *item;
4940 MENUITEMINFOW mii;
4942 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4944 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4946 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4947 return SetMenuItemInfo_common(item, &mii, TRUE);
4950 /**********************************************************************
4951 * CheckMenuRadioItem (USER32.@)
4954 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4955 UINT first, UINT last, UINT check,
4956 UINT bypos)
4958 BOOL done = FALSE;
4959 UINT i;
4960 MENUITEM *mi_first = NULL, *mi_check;
4961 HMENU m_first, m_check;
4963 for (i = first; i <= last; i++)
4965 UINT pos = i;
4967 if (!mi_first)
4969 m_first = hMenu;
4970 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4971 if (!mi_first) continue;
4972 mi_check = mi_first;
4973 m_check = m_first;
4975 else
4977 m_check = hMenu;
4978 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4979 if (!mi_check) continue;
4982 if (m_first != m_check) continue;
4983 if (mi_check->fType == MFT_SEPARATOR) continue;
4985 if (i == check)
4987 mi_check->fType |= MFT_RADIOCHECK;
4988 mi_check->fState |= MFS_CHECKED;
4989 done = TRUE;
4991 else
4993 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4994 mi_check->fState &= ~MFS_CHECKED;
4998 return done;
5002 /**********************************************************************
5003 * GetMenuItemRect (USER32.@)
5005 * ATTENTION: Here, the returned values in rect are the screen
5006 * coordinates of the item just like if the menu was
5007 * always on the upper left side of the application.
5010 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5011 LPRECT rect)
5013 POPUPMENU *itemMenu;
5014 MENUITEM *item;
5015 HWND referenceHwnd;
5017 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5019 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5020 referenceHwnd = hwnd;
5022 if(!hwnd)
5024 itemMenu = MENU_GetMenu(hMenu);
5025 if (itemMenu == NULL)
5026 return FALSE;
5028 if(itemMenu->hWnd == 0)
5029 return FALSE;
5030 referenceHwnd = itemMenu->hWnd;
5033 if ((rect == NULL) || (item == NULL))
5034 return FALSE;
5036 *rect = item->rect;
5038 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5040 return TRUE;
5043 /**********************************************************************
5044 * SetMenuInfo (USER32.@)
5046 * FIXME
5047 * actually use the items to draw the menu
5048 * (recalculate and/or redraw)
5050 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5052 POPUPMENU *menu;
5053 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5055 if (lpmi->fMask & MIM_BACKGROUND)
5056 menu->hbrBack = lpmi->hbrBack;
5058 if (lpmi->fMask & MIM_HELPID)
5059 menu->dwContextHelpID = lpmi->dwContextHelpID;
5061 if (lpmi->fMask & MIM_MAXHEIGHT)
5062 menu->cyMax = lpmi->cyMax;
5064 if (lpmi->fMask & MIM_MENUDATA)
5065 menu->dwMenuData = lpmi->dwMenuData;
5067 if (lpmi->fMask & MIM_STYLE)
5068 menu->dwStyle = lpmi->dwStyle;
5070 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5071 int i;
5072 MENUITEM *item = menu->items;
5073 for( i = menu->nItems; i; i--, item++)
5074 if( item->fType & MF_POPUP)
5075 menu_SetMenuInfo( item->hSubMenu, lpmi);
5077 return TRUE;
5080 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5082 TRACE("(%p %p)\n", hMenu, lpmi);
5083 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5084 if( lpmi->fMask & MIM_STYLE) {
5085 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5086 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5087 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5089 return TRUE;
5091 SetLastError( ERROR_INVALID_PARAMETER);
5092 return FALSE;
5095 /**********************************************************************
5096 * GetMenuInfo (USER32.@)
5098 * NOTES
5099 * win98/NT5.0
5102 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5103 { POPUPMENU *menu;
5105 TRACE("(%p %p)\n", hMenu, lpmi);
5107 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5110 if (lpmi->fMask & MIM_BACKGROUND)
5111 lpmi->hbrBack = menu->hbrBack;
5113 if (lpmi->fMask & MIM_HELPID)
5114 lpmi->dwContextHelpID = menu->dwContextHelpID;
5116 if (lpmi->fMask & MIM_MAXHEIGHT)
5117 lpmi->cyMax = menu->cyMax;
5119 if (lpmi->fMask & MIM_MENUDATA)
5120 lpmi->dwMenuData = menu->dwMenuData;
5122 if (lpmi->fMask & MIM_STYLE)
5123 lpmi->dwStyle = menu->dwStyle;
5125 return TRUE;
5127 SetLastError( ERROR_INVALID_PARAMETER);
5128 return FALSE;
5132 /**********************************************************************
5133 * SetMenuContextHelpId (USER32.@)
5135 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5137 LPPOPUPMENU menu;
5139 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5141 if ((menu = MENU_GetMenu(hMenu)))
5143 menu->dwContextHelpID = dwContextHelpID;
5144 return TRUE;
5146 return FALSE;
5150 /**********************************************************************
5151 * GetMenuContextHelpId (USER32.@)
5153 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5155 LPPOPUPMENU menu;
5157 TRACE("(%p)\n", hMenu);
5159 if ((menu = MENU_GetMenu(hMenu)))
5161 return menu->dwContextHelpID;
5163 return 0;
5166 /**********************************************************************
5167 * MenuItemFromPoint (USER32.@)
5169 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5171 POPUPMENU *menu = MENU_GetMenu(hMenu);
5172 UINT pos;
5174 /*FIXME: Do we have to handle hWnd here? */
5175 if (!menu) return -1;
5176 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5177 return pos;
5181 /**********************************************************************
5182 * translate_accelerator
5184 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5185 BYTE fVirt, WORD key, WORD cmd )
5187 INT mask = 0;
5188 UINT mesg = 0;
5190 if (wParam != key) return FALSE;
5192 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5193 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5194 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5196 if (message == WM_CHAR || message == WM_SYSCHAR)
5198 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5200 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5201 goto found;
5204 else
5206 if(fVirt & FVIRTKEY)
5208 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5209 wParam, 0xff & HIWORD(lParam));
5211 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5212 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5214 else
5216 if (!(lParam & 0x01000000)) /* no special_key */
5218 if ((fVirt & FALT) && (lParam & 0x20000000))
5219 { /* ^^ ALT pressed */
5220 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5221 goto found;
5226 return FALSE;
5228 found:
5229 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5230 mesg = 1;
5231 else
5233 HMENU hMenu, hSubMenu, hSysMenu;
5234 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5236 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5237 hSysMenu = get_win_sys_menu( hWnd );
5239 /* find menu item and ask application to initialize it */
5240 /* 1. in the system menu */
5241 hSubMenu = hSysMenu;
5242 nPos = cmd;
5243 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5245 if (GetCapture())
5246 mesg = 2;
5247 if (!IsWindowEnabled(hWnd))
5248 mesg = 3;
5249 else
5251 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5252 if(hSubMenu != hSysMenu)
5254 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5255 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5256 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5258 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5261 else /* 2. in the window's menu */
5263 hSubMenu = hMenu;
5264 nPos = cmd;
5265 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5267 if (GetCapture())
5268 mesg = 2;
5269 if (!IsWindowEnabled(hWnd))
5270 mesg = 3;
5271 else
5273 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5274 if(hSubMenu != hMenu)
5276 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5277 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5278 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5280 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5285 if (mesg == 0)
5287 if (uSysStat != (UINT)-1)
5289 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5290 mesg=4;
5291 else
5292 mesg=WM_SYSCOMMAND;
5294 else
5296 if (uStat != (UINT)-1)
5298 if (IsIconic(hWnd))
5299 mesg=5;
5300 else
5302 if (uStat & (MF_DISABLED|MF_GRAYED))
5303 mesg=6;
5304 else
5305 mesg=WM_COMMAND;
5308 else
5309 mesg=WM_COMMAND;
5314 if( mesg==WM_COMMAND )
5316 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5317 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5319 else if( mesg==WM_SYSCOMMAND )
5321 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5322 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5324 else
5326 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5327 * #0: unknown (please report!)
5328 * #1: for WM_KEYUP,WM_SYSKEYUP
5329 * #2: mouse is captured
5330 * #3: window is disabled
5331 * #4: it's a disabled system menu option
5332 * #5: it's a menu option, but window is iconic
5333 * #6: it's a menu option, but disabled
5335 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5336 if(mesg==0)
5337 ERR_(accel)(" unknown reason - please report!\n");
5339 return TRUE;
5342 /**********************************************************************
5343 * TranslateAcceleratorA (USER32.@)
5344 * TranslateAccelerator (USER32.@)
5346 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5348 switch (msg->message)
5350 case WM_KEYDOWN:
5351 case WM_SYSKEYDOWN:
5352 return TranslateAcceleratorW( hWnd, hAccel, msg );
5354 case WM_CHAR:
5355 case WM_SYSCHAR:
5357 MSG msgW = *msg;
5358 char ch = LOWORD(msg->wParam);
5359 WCHAR wch;
5360 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5361 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5362 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5365 default:
5366 return 0;
5370 /**********************************************************************
5371 * TranslateAcceleratorW (USER32.@)
5373 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5375 ACCEL data[32], *ptr = data;
5376 int i, count;
5378 if (!hWnd) return 0;
5380 if (msg->message != WM_KEYDOWN &&
5381 msg->message != WM_SYSKEYDOWN &&
5382 msg->message != WM_CHAR &&
5383 msg->message != WM_SYSCHAR)
5384 return 0;
5386 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5387 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5389 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5390 if (count > sizeof(data)/sizeof(data[0]))
5392 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5394 count = CopyAcceleratorTableW( hAccel, ptr, count );
5395 for (i = 0; i < count; i++)
5397 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5398 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5399 break;
5401 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5402 return (i < count);