services: Check for services without lpBinaryPathName in get_winedevice_process.
[wine.git] / dlls / user32 / menu.c
blob45ce7f54a473e6c8ea2c3aab726cafbe66d53c81
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* Menu item structure */
66 typedef struct {
67 /* ----------- MENUITEMINFO Stuff ----------- */
68 UINT fType; /* Item type. */
69 UINT fState; /* Item state. */
70 UINT_PTR wID; /* Item id. */
71 HMENU hSubMenu; /* Pop-up menu. */
72 HBITMAP hCheckBit; /* Bitmap when checked. */
73 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
74 LPWSTR text; /* Item text. */
75 ULONG_PTR dwItemData; /* Application defined. */
76 LPWSTR dwTypeData; /* depends on fMask */
77 HBITMAP hbmpItem; /* bitmap */
78 /* ----------- Wine stuff ----------- */
79 RECT rect; /* Item area (relative to menu window) */
80 UINT xTab; /* X position of text after Tab */
81 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
82 * bitmap */
83 } MENUITEM;
85 /* Popup menu structure */
86 typedef struct {
87 struct user_object obj;
88 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
89 WORD Width; /* Width of the whole menu */
90 WORD Height; /* Height of the whole menu */
91 UINT nItems; /* Number of items in the menu */
92 HWND hWnd; /* Window containing the menu */
93 MENUITEM *items; /* Array of menu items */
94 UINT FocusedItem; /* Currently focused item */
95 HWND hwndOwner; /* window receiving the messages for ownerdraw */
96 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
97 BOOL bScrolling; /* Scroll arrows are active */
98 UINT nScrollPos; /* Current scroll position */
99 UINT nTotalHeight; /* Total height of menu items inside menu */
100 /* ------------ MENUINFO members ------ */
101 DWORD dwStyle; /* Extended menu style */
102 UINT cyMax; /* max height of the whole menu, 0 is screen height */
103 HBRUSH hbrBack; /* brush for menu background */
104 DWORD dwContextHelpID;
105 DWORD dwMenuData; /* application defined value */
106 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
107 WORD textOffset; /* Offset of text when items have both bitmaps and text */
108 } POPUPMENU, *LPPOPUPMENU;
110 /* internal flags for menu tracking */
112 #define TF_ENDMENU 0x10000
113 #define TF_SUSPENDPOPUP 0x20000
114 #define TF_SKIPREMOVE 0x40000
116 typedef struct
118 UINT trackFlags;
119 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
120 HMENU hTopMenu; /* initial menu */
121 HWND hOwnerWnd; /* where notifications are sent */
122 POINT pt;
123 } MTRACKER;
125 #define ITEM_PREV -1
126 #define ITEM_NEXT 1
128 /* Internal MENU_TrackMenu() flags */
129 #define TPM_INTERNAL 0xF0000000
130 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
131 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
133 /* Space between 2 columns */
134 #define MENU_COL_SPACE 4
136 /* top and bottom margins for popup menus */
137 #define MENU_TOP_MARGIN 3
138 #define MENU_BOTTOM_MARGIN 2
140 /* maximum allowed depth of any branch in the menu tree.
141 * This value is slightly larger than in windows (25) to
142 * stay on the safe side. */
143 #define MAXMENUDEPTH 30
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
152 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
153 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define MENUITEMINFO_TYPE_MASK \
159 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
160 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
161 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
162 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
163 #define STATE_MASK (~TYPE_MASK)
164 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
166 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
168 static SIZE menucharsize;
169 static UINT ODitemheight; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup;
174 static HMENU top_popup_hmenu;
176 /* Flag set by EndMenu() to force an exit from menu tracking */
177 static BOOL fEndMenu = FALSE;
179 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
181 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
183 /*********************************************************************
184 * menu class descriptor
186 const struct builtin_class_descr MENU_builtin_class =
188 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
189 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
190 WINPROC_MENU, /* proc */
191 sizeof(HMENU), /* extra */
192 IDC_ARROW, /* cursor */
193 (HBRUSH)(COLOR_MENU+1) /* brush */
197 /***********************************************************************
198 * debug_print_menuitem
200 * Print a menuitem in readable form.
203 #define debug_print_menuitem(pre, mp, post) \
204 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
206 #define MENUOUT(text) \
207 TRACE("%s%s", (count++ ? "," : ""), (text))
209 #define MENUFLAG(bit,text) \
210 do { \
211 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
212 } while (0)
214 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
215 const char *postfix)
217 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
218 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
219 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
220 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
221 TRACE("%s ", prefix);
222 if (mp) {
223 UINT flags = mp->fType;
224 TRACE( "{ ID=0x%lx", mp->wID);
225 if ( mp->hSubMenu)
226 TRACE( ", Sub=%p", mp->hSubMenu);
227 if (flags) {
228 int count = 0;
229 TRACE( ", fType=");
230 MENUFLAG( MFT_SEPARATOR, "sep");
231 MENUFLAG( MFT_OWNERDRAW, "own");
232 MENUFLAG( MFT_BITMAP, "bit");
233 MENUFLAG(MF_POPUP, "pop");
234 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
235 MENUFLAG(MFT_MENUBREAK, "brk");
236 MENUFLAG(MFT_RADIOCHECK, "radio");
237 MENUFLAG(MFT_RIGHTORDER, "rorder");
238 MENUFLAG(MF_SYSMENU, "sys");
239 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
240 if (flags)
241 TRACE( "+0x%x", flags);
243 flags = mp->fState;
244 if (flags) {
245 int count = 0;
246 TRACE( ", State=");
247 MENUFLAG(MFS_GRAYED, "grey");
248 MENUFLAG(MFS_DEFAULT, "default");
249 MENUFLAG(MFS_DISABLED, "dis");
250 MENUFLAG(MFS_CHECKED, "check");
251 MENUFLAG(MFS_HILITE, "hi");
252 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
253 MENUFLAG(MF_MOUSESELECT, "mouse");
254 if (flags)
255 TRACE( "+0x%x", flags);
257 if (mp->hCheckBit)
258 TRACE( ", Chk=%p", mp->hCheckBit);
259 if (mp->hUnCheckBit)
260 TRACE( ", Unc=%p", mp->hUnCheckBit);
261 if (mp->text)
262 TRACE( ", Text=%s", debugstr_w(mp->text));
263 if (mp->dwItemData)
264 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
265 if (mp->hbmpItem)
267 if( IS_MAGIC_BITMAP(mp->hbmpItem))
268 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
269 else
270 TRACE( ", hbitmap=%p", mp->hbmpItem);
272 TRACE( " }");
273 } else
274 TRACE( "NULL");
275 TRACE(" %s\n", postfix);
278 #undef MENUOUT
279 #undef MENUFLAG
282 /***********************************************************************
283 * MENU_GetMenu
285 * Validate the given menu handle and returns the menu structure pointer.
287 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
289 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
291 if (menu == OBJ_OTHER_PROCESS)
293 WARN( "other process menu %p?\n", hMenu);
294 return NULL;
296 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
297 else WARN("invalid menu handle=%p\n", hMenu);
298 return menu;
301 /***********************************************************************
302 * get_win_sys_menu
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
308 HMENU ret = 0;
309 WND *win = WIN_GetPtr( hwnd );
310 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 ret = win->hSysMenu;
313 WIN_ReleasePtr( win );
315 return ret;
318 /***********************************************************************
319 * get_menu_font
321 static HFONT get_menu_font( BOOL bold )
323 static HFONT hMenuFont, hMenuFontBold;
325 HFONT ret = bold ? hMenuFontBold : hMenuFont;
327 if (!ret)
329 NONCLIENTMETRICSW ncm;
330 HFONT prev;
332 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
335 if (bold)
337 ncm.lfMenuFont.lfWeight += 300;
338 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
340 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
341 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
342 ret, NULL );
343 if (prev)
345 /* another thread beat us to it */
346 DeleteObject( ret );
347 ret = prev;
350 return ret;
353 /***********************************************************************
354 * get_arrow_bitmap
356 static HBITMAP get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap;
360 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
361 return arrow_bitmap;
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap;
371 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
372 return arrow_bitmap;
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap;
382 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
383 return arrow_bitmap;
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap;
393 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
394 return arrow_bitmap;
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap;
404 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
405 return arrow_bitmap;
408 /***********************************************************************
409 * MENU_CopySysPopup
411 * Return the default system menu.
413 static HMENU MENU_CopySysPopup(BOOL mdi)
415 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
416 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
417 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
419 if( hMenu ) {
420 MENUINFO minfo;
421 MENUITEMINFOW miteminfo;
422 POPUPMENU* menu = MENU_GetMenu(hMenu);
423 menu->wFlags |= MF_SYSMENU | MF_POPUP;
424 /* decorate the menu with bitmaps */
425 minfo.cbSize = sizeof( MENUINFO);
426 minfo.dwStyle = MNS_CHECKORBMP;
427 minfo.fMask = MIM_STYLE;
428 SetMenuInfo( hMenu, &minfo);
429 miteminfo.cbSize = sizeof( MENUITEMINFOW);
430 miteminfo.fMask = MIIM_BITMAP;
431 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
432 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
433 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
434 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
435 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
436 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
437 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
438 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
439 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
441 else
442 ERR("Unable to load default system menu\n" );
444 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
446 return hMenu;
450 /**********************************************************************
451 * MENU_GetSysMenu
453 * Create a copy of the system menu. System menu in Windows is
454 * a special menu bar with the single entry - system menu popup.
455 * This popup is presented to the outside world as a "system menu".
456 * However, the real system menu handle is sometimes seen in the
457 * WM_MENUSELECT parameters (and Word 6 likes it this way).
459 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
461 HMENU hMenu;
463 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
464 if ((hMenu = CreateMenu()))
466 POPUPMENU *menu = MENU_GetMenu(hMenu);
467 menu->wFlags = MF_SYSMENU;
468 menu->hWnd = WIN_GetFullHandle( hWnd );
469 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
471 if (!hPopupMenu)
473 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
474 hPopupMenu = MENU_CopySysPopup(TRUE);
475 else
476 hPopupMenu = MENU_CopySysPopup(FALSE);
479 if (hPopupMenu)
481 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
482 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
484 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
485 (UINT_PTR)hPopupMenu, NULL );
487 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
488 menu->items[0].fState = 0;
489 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
491 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
492 return hMenu;
494 DestroyMenu( hMenu );
496 ERR("failed to load system menu!\n");
497 return 0;
501 /***********************************************************************
502 * MENU_InitSysMenuPopup
504 * Grey the appropriate items in System menu.
506 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
508 BOOL gray;
510 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
511 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = ((style & WS_MAXIMIZE) != 0);
513 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
514 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
515 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
517 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
518 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
519 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
520 gray = (clsStyle & CS_NOCLOSE) != 0;
522 /* The menu item must keep its state if it's disabled */
523 if(gray)
524 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
528 /******************************************************************************
530 * UINT MENU_GetStartOfNextColumn(
531 * HMENU hMenu )
533 *****************************************************************************/
535 static UINT MENU_GetStartOfNextColumn(
536 HMENU hMenu )
538 POPUPMENU *menu = MENU_GetMenu(hMenu);
539 UINT i;
541 if(!menu)
542 return NO_SELECTED_ITEM;
544 i = menu->FocusedItem + 1;
545 if( i == NO_SELECTED_ITEM )
546 return i;
548 for( ; i < menu->nItems; ++i ) {
549 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
550 return i;
553 return NO_SELECTED_ITEM;
557 /******************************************************************************
559 * UINT MENU_GetStartOfPrevColumn(
560 * HMENU hMenu )
562 *****************************************************************************/
564 static UINT MENU_GetStartOfPrevColumn(
565 HMENU hMenu )
567 POPUPMENU *menu = MENU_GetMenu(hMenu);
568 UINT i;
570 if( !menu )
571 return NO_SELECTED_ITEM;
573 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
574 return NO_SELECTED_ITEM;
576 /* Find the start of the column */
578 for(i = menu->FocusedItem; i != 0 &&
579 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
580 --i); /* empty */
582 if(i == 0)
583 return NO_SELECTED_ITEM;
585 for(--i; i != 0; --i) {
586 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
587 break;
590 TRACE("ret %d.\n", i );
592 return i;
597 /***********************************************************************
598 * MENU_FindItem
600 * Find a menu item. Return a pointer on the item, and modifies *hmenu
601 * in case the item was in a sub-menu.
603 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
605 POPUPMENU *menu;
606 MENUITEM *fallback = NULL;
607 UINT fallback_pos = 0;
608 UINT i;
610 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
611 if (wFlags & MF_BYPOSITION)
613 if (*nPos >= menu->nItems) return NULL;
614 return &menu->items[*nPos];
616 else
618 MENUITEM *item = menu->items;
619 for (i = 0; i < menu->nItems; i++, item++)
621 if (item->fType & MF_POPUP)
623 HMENU hsubmenu = item->hSubMenu;
624 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
625 if (subitem)
627 *hmenu = hsubmenu;
628 return subitem;
630 else if (item->wID == *nPos)
632 /* fallback to this item if nothing else found */
633 fallback_pos = i;
634 fallback = item;
637 else if (item->wID == *nPos)
639 *nPos = i;
640 return item;
645 if (fallback)
646 *nPos = fallback_pos;
648 return fallback;
651 /***********************************************************************
652 * MENU_FindSubMenu
654 * Find a Sub menu. Return the position of the submenu, and modifies
655 * *hmenu in case it is found in another sub-menu.
656 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
658 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
660 POPUPMENU *menu;
661 UINT i;
662 MENUITEM *item;
663 if (((*hmenu)==(HMENU)0xffff) ||
664 (!(menu = MENU_GetMenu(*hmenu))))
665 return NO_SELECTED_ITEM;
666 item = menu->items;
667 for (i = 0; i < menu->nItems; i++, item++) {
668 if(!(item->fType & MF_POPUP)) continue;
669 if (item->hSubMenu == hSubTarget) {
670 return i;
672 else {
673 HMENU hsubmenu = item->hSubMenu;
674 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
675 if (pos != NO_SELECTED_ITEM) {
676 *hmenu = hsubmenu;
677 return pos;
681 return NO_SELECTED_ITEM;
684 /***********************************************************************
685 * MENU_FreeItemData
687 static void MENU_FreeItemData( MENUITEM* item )
689 /* delete text */
690 HeapFree( GetProcessHeap(), 0, item->text );
693 /***********************************************************************
694 * MENU_AdjustMenuItemRect
696 * Adjust menu item rectangle according to scrolling state.
698 static void
699 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
701 if (menu->bScrolling)
703 UINT arrow_bitmap_height;
704 BITMAP bmp;
706 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
707 arrow_bitmap_height = bmp.bmHeight;
708 rect->top += arrow_bitmap_height - menu->nScrollPos;
709 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
714 /***********************************************************************
715 * MENU_FindItemByCoords
717 * Find the item at the specified coordinates (screen coords). Does
718 * not work for child windows and therefore should not be called for
719 * an arbitrary system menu.
721 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
722 POINT pt, UINT *pos )
724 MENUITEM *item;
725 UINT i;
726 RECT rect;
728 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
729 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
730 else pt.x -= rect.left;
731 pt.y -= rect.top;
732 item = menu->items;
733 for (i = 0; i < menu->nItems; i++, item++)
735 rect = item->rect;
736 MENU_AdjustMenuItemRect(menu, &rect);
737 if (PtInRect(&rect, pt))
739 if (pos) *pos = i;
740 return item;
743 return NULL;
747 /***********************************************************************
748 * MENU_FindItemByKey
750 * Find the menu item selected by a key press.
751 * Return item id, -1 if none, -2 if we should close the menu.
753 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
754 WCHAR key, BOOL forceMenuChar )
756 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
758 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
760 if (hmenu)
762 POPUPMENU *menu = MENU_GetMenu( hmenu );
763 MENUITEM *item = menu->items;
764 LRESULT menuchar;
766 if( !forceMenuChar )
768 UINT i;
769 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
771 for (i = 0; i < menu->nItems; i++, item++)
773 if( item->text)
775 const WCHAR *p = item->text - 2;
778 const WCHAR *q = p + 2;
779 p = strchrW (q, '&');
780 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
782 while (p != NULL && p [1] == '&');
783 if (p && (toupperW(p[1]) == toupperW(key))) return i;
787 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
788 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
789 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
790 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
792 return (UINT)(-1);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
802 HWND hwndOwner)
804 BITMAP bm;
805 HBITMAP bmp = lpitem->hbmpItem;
807 size->cx = size->cy = 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR) bmp )
812 case (INT_PTR)HBMMENU_CALLBACK:
814 MEASUREITEMSTRUCT measItem;
815 measItem.CtlType = ODT_MENU;
816 measItem.CtlID = 0;
817 measItem.itemID = lpitem->wID;
818 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
819 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
820 measItem.itemData = lpitem->dwItemData;
821 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
822 size->cx = measItem.itemWidth;
823 size->cy = measItem.itemHeight;
824 return;
826 break;
827 case (INT_PTR)HBMMENU_SYSTEM:
828 if (lpitem->dwItemData)
830 bmp = (HBITMAP)lpitem->dwItemData;
831 break;
833 /* fall through */
834 case (INT_PTR)HBMMENU_MBAR_RESTORE:
835 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
836 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
837 case (INT_PTR)HBMMENU_MBAR_CLOSE:
838 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
839 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
840 size->cy = size->cx;
841 return;
842 case (INT_PTR)HBMMENU_POPUP_CLOSE:
843 case (INT_PTR)HBMMENU_POPUP_RESTORE:
844 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
845 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
846 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
847 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
848 return;
850 if (GetObjectW(bmp, sizeof(bm), &bm ))
852 size->cx = bm.bmWidth;
853 size->cy = bm.bmHeight;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
863 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
865 BITMAP bm;
866 DWORD rop;
867 HDC hdcMem;
868 HBITMAP bmp;
869 int w = rect->right - rect->left;
870 int h = rect->bottom - rect->top;
871 int bmp_xoffset = 0;
872 int left, top;
873 HBITMAP hbmToDraw = lpitem->hbmpItem;
874 bmp = hbmToDraw;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw))
879 UINT flags = 0;
880 WCHAR bmchr = 0;
881 RECT r;
883 switch((INT_PTR)hbmToDraw)
885 case (INT_PTR)HBMMENU_SYSTEM:
886 if (lpitem->dwItemData)
888 bmp = (HBITMAP)lpitem->dwItemData;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
891 else
893 static HBITMAP hBmpSysMenu;
895 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
896 bmp = hBmpSysMenu;
897 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset = bm.bmWidth / 2;
900 bm.bmWidth -= bmp_xoffset;
902 goto got_bitmap;
903 case (INT_PTR)HBMMENU_MBAR_RESTORE:
904 flags = DFCS_CAPTIONRESTORE;
905 break;
906 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
907 flags = DFCS_CAPTIONMIN;
908 break;
909 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
910 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
911 break;
912 case (INT_PTR)HBMMENU_MBAR_CLOSE:
913 flags = DFCS_CAPTIONCLOSE;
914 break;
915 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
916 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
917 break;
918 case (INT_PTR)HBMMENU_CALLBACK:
920 DRAWITEMSTRUCT drawItem;
921 drawItem.CtlType = ODT_MENU;
922 drawItem.CtlID = 0;
923 drawItem.itemID = lpitem->wID;
924 drawItem.itemAction = odaction;
925 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
926 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
927 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
928 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
929 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
930 drawItem.hwndItem = (HWND)hmenu;
931 drawItem.hDC = hdc;
932 drawItem.itemData = lpitem->dwItemData;
933 drawItem.rcItem = *rect;
934 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
935 return;
937 break;
938 case (INT_PTR)HBMMENU_POPUP_CLOSE:
939 bmchr = 0x72;
940 break;
941 case (INT_PTR)HBMMENU_POPUP_RESTORE:
942 bmchr = 0x32;
943 break;
944 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
945 bmchr = 0x31;
946 break;
947 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
948 bmchr = 0x30;
949 break;
950 default:
951 FIXME("Magic %p not implemented\n", hbmToDraw);
952 return;
954 if (bmchr)
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont, hfontsav;
959 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
960 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont.lfHeight = min( h, w) - 5 ;
963 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
964 hfont = CreateFontIndirectW( &logfont);
965 hfontsav = SelectObject(hdc, hfont);
966 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
967 SelectObject(hdc, hfontsav);
968 DeleteObject( hfont);
970 else
972 r = *rect;
973 InflateRect( &r, -1, -1 );
974 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
975 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
977 return;
980 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
982 got_bitmap:
983 hdcMem = CreateCompatibleDC( hdc );
984 SelectObject( hdcMem, bmp );
986 /* handle fontsize > bitmap_height */
987 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
988 left=rect->left;
989 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
990 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
991 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
992 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
993 DeleteDC( hdcMem );
997 /***********************************************************************
998 * MENU_CalcItemSize
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1003 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1005 WCHAR *p;
1006 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1007 UINT arrow_bitmap_width;
1008 BITMAP bm;
1009 INT itemheight;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1013 (menuBar ? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1016 arrow_bitmap_width = bm.bmWidth;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize.cx ) {
1020 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight = HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1028 if (lpitem->fType & MF_OWNERDRAW)
1030 MEASUREITEMSTRUCT mis;
1031 mis.CtlType = ODT_MENU;
1032 mis.CtlID = 0;
1033 mis.itemID = lpitem->wID;
1034 mis.itemData = lpitem->dwItemData;
1035 mis.itemHeight = ODitemheight;
1036 mis.itemWidth = 0;
1037 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1038 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1042 if (menuBar) {
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1046 } else
1047 lpitem->rect.bottom += mis.itemHeight;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1051 lpitem->rect.bottom-lpitem->rect.top);
1052 return;
1055 if (lpitem->fType & MF_SEPARATOR)
1057 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1058 if( !menuBar)
1059 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1060 return;
1063 itemheight = 0;
1064 lpitem->xTab = 0;
1066 if (!menuBar) {
1067 if (lpitem->hbmpItem) {
1068 SIZE size;
1070 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem->bmpsize = size;
1074 lppop->textOffset = max( lppop->textOffset, size.cx);
1075 lpitem->rect.right += size.cx + 2;
1076 itemheight = size.cy + 2;
1078 if( !(lppop->dwStyle & MNS_NOCHECK))
1079 lpitem->rect.right += check_bitmap_width;
1080 lpitem->rect.right += 4 + menucharsize.cx;
1081 lpitem->xTab = lpitem->rect.right;
1082 lpitem->rect.right += arrow_bitmap_width;
1083 } else if (lpitem->hbmpItem) { /* menuBar */
1084 SIZE size;
1086 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1087 lpitem->bmpsize = size;
1088 lpitem->rect.right += size.cx;
1089 if( lpitem->text) lpitem->rect.right += 2;
1090 itemheight = size.cy;
1093 /* it must be a text item - unless it's the system menu */
1094 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1095 HFONT hfontOld = NULL;
1096 RECT rc = lpitem->rect;
1097 LONG txtheight, txtwidth;
1099 if ( lpitem->fState & MFS_DEFAULT ) {
1100 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1102 if (menuBar) {
1103 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1104 DT_SINGLELINE|DT_CALCRECT);
1105 lpitem->rect.right += rc.right - rc.left;
1106 itemheight = max( max( itemheight, txtheight),
1107 GetSystemMetrics( SM_CYMENU) - 1);
1108 lpitem->rect.right += 2 * menucharsize.cx;
1109 } else {
1110 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1111 RECT tmprc = rc;
1112 LONG tmpheight;
1113 int n = (int)( p - lpitem->text);
1114 /* Item contains a tab (only meaningful in popup menus) */
1115 /* get text size before the tab */
1116 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1117 DT_SINGLELINE|DT_CALCRECT);
1118 txtwidth = rc.right - rc.left;
1119 p += 1; /* advance past the Tab */
1120 /* get text size after the tab */
1121 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1122 DT_SINGLELINE|DT_CALCRECT);
1123 lpitem->xTab += txtwidth;
1124 txtheight = max( txtheight, tmpheight);
1125 txtwidth += menucharsize.cx + /* space for the tab */
1126 tmprc.right - tmprc.left; /* space for the short cut */
1127 } else {
1128 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 txtwidth = rc.right - rc.left;
1131 lpitem->xTab += txtwidth;
1133 lpitem->rect.right += 2 + txtwidth;
1134 itemheight = max( itemheight,
1135 max( txtheight + 2, menucharsize.cy + 4));
1137 if (hfontOld) SelectObject (hdc, hfontOld);
1138 } else if( menuBar) {
1139 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1141 lpitem->rect.bottom += itemheight;
1142 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1146 /***********************************************************************
1147 * MENU_GetMaxPopupHeight
1149 static UINT
1150 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1152 if (lppop->cyMax)
1153 return lppop->cyMax;
1154 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1158 /***********************************************************************
1159 * MENU_PopupMenuCalcSize
1161 * Calculate the size of a popup menu.
1163 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1165 MENUITEM *lpitem;
1166 HDC hdc;
1167 UINT start, i;
1168 BOOL textandbmp = FALSE;
1169 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1171 lppop->Width = lppop->Height = 0;
1172 if (lppop->nItems == 0) return;
1173 hdc = GetDC( 0 );
1175 SelectObject( hdc, get_menu_font(FALSE));
1177 start = 0;
1178 maxX = 2 + 1;
1180 lppop->textOffset = 0;
1182 while (start < lppop->nItems)
1184 lpitem = &lppop->items[start];
1185 orgX = maxX;
1186 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1187 orgX += MENU_COL_SPACE;
1188 orgY = MENU_TOP_MARGIN;
1190 maxTab = maxTabWidth = 0;
1191 /* Parse items until column break or end of menu */
1192 for (i = start; i < lppop->nItems; i++, lpitem++)
1194 if ((i != start) &&
1195 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1197 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1198 maxX = max( maxX, lpitem->rect.right );
1199 orgY = lpitem->rect.bottom;
1200 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1202 maxTab = max( maxTab, lpitem->xTab );
1203 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1205 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX = max( maxX, maxTab + maxTabWidth );
1210 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1212 lpitem->rect.right = maxX;
1213 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1214 lpitem->xTab = maxTab;
1217 lppop->Height = max( lppop->Height, orgY );
1220 lppop->Width = maxX;
1221 /* if none of the items have both text and bitmap then
1222 * the text and bitmaps are all aligned on the left. If there is at
1223 * least one item with both text and bitmap then bitmaps are
1224 * on the left and texts left aligned with the right hand side
1225 * of the bitmaps */
1226 if( !textandbmp) lppop->textOffset = 0;
1228 /* space for 3d border */
1229 lppop->Height += MENU_BOTTOM_MARGIN;
1230 lppop->Width += 2;
1232 /* Adjust popup height if it exceeds maximum */
1233 maxHeight = MENU_GetMaxPopupHeight(lppop);
1234 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1235 if (lppop->Height >= maxHeight)
1237 lppop->Height = maxHeight;
1238 lppop->bScrolling = TRUE;
1240 else
1242 lppop->bScrolling = FALSE;
1245 ReleaseDC( 0, hdc );
1249 /***********************************************************************
1250 * MENU_MenuBarCalcSize
1252 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1253 * height is off by 1 pixel which causes lengthy window relocations when
1254 * active document window is maximized/restored.
1256 * Calculate the size of the menu bar.
1258 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1259 LPPOPUPMENU lppop, HWND hwndOwner )
1261 MENUITEM *lpitem;
1262 UINT start, i, helpPos;
1263 int orgX, orgY, maxY;
1265 if ((lprect == NULL) || (lppop == NULL)) return;
1266 if (lppop->nItems == 0) return;
1267 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1268 lppop->Width = lprect->right - lprect->left;
1269 lppop->Height = 0;
1270 maxY = lprect->top+1;
1271 start = 0;
1272 helpPos = ~0U;
1273 lppop->textOffset = 0;
1274 while (start < lppop->nItems)
1276 lpitem = &lppop->items[start];
1277 orgX = lprect->left;
1278 orgY = maxY;
1280 /* Parse items until line break or end of menu */
1281 for (i = start; i < lppop->nItems; i++, lpitem++)
1283 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1284 if ((i != start) &&
1285 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1287 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1288 debug_print_menuitem (" item: ", lpitem, "");
1289 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1291 if (lpitem->rect.right > lprect->right)
1293 if (i != start) break;
1294 else lpitem->rect.right = lprect->right;
1296 maxY = max( maxY, lpitem->rect.bottom );
1297 orgX = lpitem->rect.right;
1300 /* Finish the line (set all items to the largest height found) */
1301 while (start < i) lppop->items[start++].rect.bottom = maxY;
1304 lprect->bottom = maxY;
1305 lppop->Height = lprect->bottom - lprect->top;
1307 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1308 /* the last item (if several lines, only move the last line) */
1309 if (helpPos == ~0U) return;
1310 lpitem = &lppop->items[lppop->nItems-1];
1311 orgY = lpitem->rect.top;
1312 orgX = lprect->right;
1313 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1314 if (lpitem->rect.top != orgY) break; /* Other line */
1315 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1316 lpitem->rect.left += orgX - lpitem->rect.right;
1317 lpitem->rect.right = orgX;
1318 orgX = lpitem->rect.left;
1323 /***********************************************************************
1324 * MENU_DrawScrollArrows
1326 * Draw scroll arrows.
1328 static void
1329 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1331 HDC hdcMem = CreateCompatibleDC(hdc);
1332 HBITMAP hOrigBitmap;
1333 UINT arrow_bitmap_width, arrow_bitmap_height;
1334 BITMAP bmp;
1335 RECT rect;
1337 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1338 arrow_bitmap_width = bmp.bmWidth;
1339 arrow_bitmap_height = bmp.bmHeight;
1342 if (lppop->nScrollPos)
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1344 else
1345 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1346 SetRect(&rect, 0, 0, lppop->Width, arrow_bitmap_height);
1347 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1348 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1349 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1350 rect.top = lppop->Height - arrow_bitmap_height;
1351 rect.bottom = lppop->Height;
1352 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1353 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1354 SelectObject(hdcMem, get_down_arrow_bitmap());
1355 else
1356 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1357 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1358 lppop->Height - arrow_bitmap_height,
1359 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1360 SelectObject(hdcMem, hOrigBitmap);
1361 DeleteDC(hdcMem);
1365 /***********************************************************************
1366 * draw_popup_arrow
1368 * Draws the popup-menu arrow.
1370 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1371 UINT arrow_bitmap_height)
1373 HDC hdcMem = CreateCompatibleDC( hdc );
1374 HBITMAP hOrigBitmap;
1376 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1377 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1378 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1379 arrow_bitmap_width, arrow_bitmap_height,
1380 hdcMem, 0, 0, SRCCOPY );
1381 SelectObject( hdcMem, hOrigBitmap );
1382 DeleteDC( hdcMem );
1384 /***********************************************************************
1385 * MENU_DrawMenuItem
1387 * Draw a single menu item.
1389 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1390 UINT height, BOOL menuBar, UINT odaction )
1392 RECT rect;
1393 BOOL flat_menu = FALSE;
1394 int bkgnd;
1395 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1396 POPUPMENU *menu = MENU_GetMenu(hmenu);
1397 RECT bmprc;
1399 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1401 if (!menuBar) {
1402 BITMAP bmp;
1403 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1404 arrow_bitmap_width = bmp.bmWidth;
1405 arrow_bitmap_height = bmp.bmHeight;
1408 if (lpitem->fType & MF_SYSMENU)
1410 if( !IsIconic(hwnd) )
1411 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1412 return;
1415 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1416 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1418 /* Setup colors */
1420 if (lpitem->fState & MF_HILITE)
1422 if(menuBar && !flat_menu) {
1423 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1424 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1425 } else {
1426 if(lpitem->fState & MF_GRAYED)
1427 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1428 else
1429 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1430 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1433 else
1435 if (lpitem->fState & MF_GRAYED)
1436 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1437 else
1438 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1439 SetBkColor( hdc, GetSysColor( bkgnd ) );
1442 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1443 rect = lpitem->rect;
1444 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1446 if (lpitem->fType & MF_OWNERDRAW)
1449 ** Experimentation under Windows reveals that an owner-drawn
1450 ** menu is given the rectangle which includes the space it requested
1451 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1452 ** and a popup-menu arrow. This is the value of lpitem->rect.
1453 ** Windows will leave all drawing to the application except for
1454 ** the popup-menu arrow. Windows always draws that itself, after
1455 ** the menu owner has finished drawing.
1457 DRAWITEMSTRUCT dis;
1458 COLORREF old_bk, old_text;
1460 dis.CtlType = ODT_MENU;
1461 dis.CtlID = 0;
1462 dis.itemID = lpitem->wID;
1463 dis.itemData = lpitem->dwItemData;
1464 dis.itemState = 0;
1465 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1466 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1467 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1468 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1469 dis.hwndItem = (HWND)hmenu;
1470 dis.hDC = hdc;
1471 dis.rcItem = rect;
1472 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1473 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1474 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1475 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1476 old_bk = GetBkColor( hdc );
1477 old_text = GetTextColor( hdc );
1478 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1479 /* Draw the popup-menu arrow */
1480 SetBkColor( hdc, old_bk );
1481 SetTextColor( hdc, old_text );
1482 if (lpitem->fType & MF_POPUP)
1483 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1484 arrow_bitmap_height);
1485 return;
1488 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1490 if (lpitem->fState & MF_HILITE)
1492 if (flat_menu)
1494 InflateRect (&rect, -1, -1);
1495 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1496 InflateRect (&rect, 1, 1);
1497 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1499 else
1501 if(menuBar)
1502 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1503 else
1504 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1507 else
1508 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1510 SetBkMode( hdc, TRANSPARENT );
1512 /* vertical separator */
1513 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1515 HPEN oldPen;
1516 RECT rc = rect;
1518 rc.left -= MENU_COL_SPACE / 2 + 1;
1519 rc.top = 3;
1520 rc.bottom = height - 3;
1521 if (flat_menu)
1523 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1524 MoveToEx( hdc, rc.left, rc.top, NULL );
1525 LineTo( hdc, rc.left, rc.bottom );
1526 SelectObject( hdc, oldPen );
1528 else
1529 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1532 /* horizontal separator */
1533 if (lpitem->fType & MF_SEPARATOR)
1535 HPEN oldPen;
1536 RECT rc = rect;
1538 rc.left++;
1539 rc.right--;
1540 rc.top = ( rc.top + rc.bottom) / 2;
1541 if (flat_menu)
1543 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1544 MoveToEx( hdc, rc.left, rc.top, NULL );
1545 LineTo( hdc, rc.right, rc.top );
1546 SelectObject( hdc, oldPen );
1548 else
1549 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1550 return;
1553 /* helper lines for debugging */
1554 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1555 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1556 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1557 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1560 if (lpitem->hbmpItem) {
1561 /* calculate the bitmap rectangle in coordinates relative
1562 * to the item rectangle */
1563 if( menuBar) {
1564 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1565 bmprc.left = 3;
1566 else
1567 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1569 else if (menu->dwStyle & MNS_NOCHECK)
1570 bmprc.left = 4;
1571 else if (menu->dwStyle & MNS_CHECKORBMP)
1572 bmprc.left = 2;
1573 else
1574 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1575 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1576 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1577 bmprc.top = 0;
1578 else
1579 bmprc.top = (rect.bottom - rect.top -
1580 lpitem->bmpsize.cy) / 2;
1581 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1584 if (!menuBar)
1586 HBITMAP bm;
1587 INT y = rect.top + rect.bottom;
1588 RECT rc = rect;
1589 BOOL checked = FALSE;
1590 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1591 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1592 /* Draw the check mark
1594 * FIXME:
1595 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1597 if( !(menu->dwStyle & MNS_NOCHECK)) {
1598 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1599 lpitem->hUnCheckBit;
1600 if (bm) /* we have a custom bitmap */
1602 HDC hdcMem = CreateCompatibleDC( hdc );
1604 SelectObject( hdcMem, bm );
1605 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1606 check_bitmap_width, check_bitmap_height,
1607 hdcMem, 0, 0, SRCCOPY );
1608 DeleteDC( hdcMem );
1609 checked = TRUE;
1611 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1613 RECT r;
1614 HBITMAP bm = CreateBitmap( check_bitmap_width,
1615 check_bitmap_height, 1, 1, NULL );
1616 HDC hdcMem = CreateCompatibleDC( hdc );
1618 SelectObject( hdcMem, bm );
1619 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1620 DrawFrameControl( hdcMem, &r, DFC_MENU,
1621 (lpitem->fType & MFT_RADIOCHECK) ?
1622 DFCS_MENUBULLET : DFCS_MENUCHECK );
1623 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1624 hdcMem, 0, 0, SRCCOPY );
1625 DeleteDC( hdcMem );
1626 DeleteObject( bm );
1627 checked = TRUE;
1630 if( lpitem->hbmpItem &&
1631 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1632 POINT origorg;
1633 /* some applications make this assumption on the DC's origin */
1634 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1635 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1636 odaction, FALSE);
1637 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1639 /* Draw the popup-menu arrow */
1640 if (lpitem->fType & MF_POPUP)
1641 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1642 arrow_bitmap_height);
1643 rect.left += 4;
1644 if( !(menu->dwStyle & MNS_NOCHECK))
1645 rect.left += check_bitmap_width;
1646 rect.right -= arrow_bitmap_width;
1648 else if( lpitem->hbmpItem)
1649 { /* Draw the bitmap */
1650 POINT origorg;
1652 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1653 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1654 odaction, menuBar);
1655 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1657 /* process text if present */
1658 if (lpitem->text)
1660 int i;
1661 HFONT hfontOld = 0;
1663 UINT uFormat = (menuBar) ?
1664 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1665 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1667 if( !(menu->dwStyle & MNS_CHECKORBMP))
1668 rect.left += menu->textOffset;
1670 if ( lpitem->fState & MFS_DEFAULT )
1672 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1675 if (menuBar) {
1676 if( lpitem->hbmpItem)
1677 rect.left += lpitem->bmpsize.cx;
1678 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1679 rect.left += menucharsize.cx;
1680 rect.right -= menucharsize.cx;
1683 for (i = 0; lpitem->text[i]; i++)
1684 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1685 break;
1687 if(lpitem->fState & MF_GRAYED)
1689 if (!(lpitem->fState & MF_HILITE) )
1691 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1692 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1693 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1694 --rect.left; --rect.top; --rect.right; --rect.bottom;
1696 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1699 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1701 /* paint the shortcut text */
1702 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1704 if (lpitem->text[i] == '\t')
1706 rect.left = lpitem->xTab;
1707 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1709 else
1711 rect.right = lpitem->xTab;
1712 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1715 if(lpitem->fState & MF_GRAYED)
1717 if (!(lpitem->fState & MF_HILITE) )
1719 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1720 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1721 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1722 --rect.left; --rect.top; --rect.right; --rect.bottom;
1724 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1726 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1729 if (hfontOld)
1730 SelectObject (hdc, hfontOld);
1735 /***********************************************************************
1736 * MENU_DrawPopupMenu
1738 * Paint a popup menu.
1740 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1742 HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1743 RECT rect;
1744 POPUPMENU *menu = MENU_GetMenu( hmenu );
1746 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1748 GetClientRect( hwnd, &rect );
1750 if (menu && menu->hbrBack) brush = menu->hbrBack;
1751 if ((hPrevBrush = SelectObject( hdc, brush ))
1752 && SelectObject( hdc, get_menu_font(FALSE) ))
1754 HPEN hPrevPen;
1756 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1758 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1759 if( hPrevPen )
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)
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 )
1802 LPPOPUPMENU lppop;
1803 HMENU hMenu = GetMenu(hwnd);
1805 lppop = MENU_GetMenu( hMenu );
1806 if (lppop == NULL || lprect == NULL)
1808 return GetSystemMetrics(SM_CYMENU);
1811 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1815 /***********************************************************************
1816 * MENU_InitPopup
1818 * Popup menu initialization before WM_ENTERMENULOOP.
1820 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1822 POPUPMENU *menu;
1823 DWORD ex_style = 0;
1825 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1827 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1829 /* store the owner for DrawItem */
1830 if (!IsWindow( hwndOwner ))
1832 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1833 return FALSE;
1835 menu->hwndOwner = hwndOwner;
1837 if (flags & TPM_LAYOUTRTL)
1838 ex_style = WS_EX_LAYOUTRTL;
1840 /* NOTE: In Windows, top menu popup is not owned. */
1841 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1842 WS_POPUP, 0, 0, 0, 0,
1843 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1844 (LPVOID)hmenu );
1845 if( !menu->hWnd ) return FALSE;
1846 return TRUE;
1850 /***********************************************************************
1851 * MENU_ShowPopup
1853 * Display a popup menu.
1855 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1856 INT x, INT y, INT xanchor, INT yanchor )
1858 POPUPMENU *menu;
1859 INT width, height;
1860 POINT pt;
1861 HMONITOR monitor;
1862 MONITORINFO info;
1864 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1865 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1867 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1868 if (menu->FocusedItem != NO_SELECTED_ITEM)
1870 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1871 menu->FocusedItem = NO_SELECTED_ITEM;
1874 menu->nScrollPos = 0;
1875 MENU_PopupMenuCalcSize( menu );
1877 /* adjust popup menu pos so that it fits within the desktop */
1879 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1880 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1882 /* FIXME: should use item rect */
1883 pt.x = x;
1884 pt.y = y;
1885 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1886 info.cbSize = sizeof(info);
1887 GetMonitorInfoW( monitor, &info );
1889 if (flags & TPM_LAYOUTRTL)
1890 flags ^= TPM_RIGHTALIGN;
1892 if( flags & TPM_RIGHTALIGN ) x -= width;
1893 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1895 if( flags & TPM_BOTTOMALIGN ) y -= height;
1896 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1898 if( x + width > info.rcWork.right)
1900 if( xanchor && x >= width - xanchor )
1901 x -= width - xanchor;
1903 if( x + width > info.rcWork.right)
1904 x = info.rcWork.right - width;
1906 if( x < info.rcWork.left ) x = info.rcWork.left;
1908 if( y + height > info.rcWork.bottom)
1910 if( yanchor && y >= height + yanchor )
1911 y -= height + yanchor;
1913 if( y + height > info.rcWork.bottom)
1914 y = info.rcWork.bottom - height;
1916 if( y < info.rcWork.top ) y = info.rcWork.top;
1918 if (!top_popup) {
1919 top_popup = menu->hWnd;
1920 top_popup_hmenu = hmenu;
1922 /* Display the window */
1924 SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, width, height,
1925 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1926 UpdateWindow( menu->hWnd );
1927 return TRUE;
1931 /***********************************************************************
1932 * MENU_EnsureMenuItemVisible
1934 static void
1935 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1937 if (lppop->bScrolling)
1939 MENUITEM *item = &lppop->items[wIndex];
1940 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1941 UINT nOldPos = lppop->nScrollPos;
1942 RECT rc;
1943 UINT arrow_bitmap_height;
1944 BITMAP bmp;
1946 GetClientRect(lppop->hWnd, &rc);
1948 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1949 arrow_bitmap_height = bmp.bmHeight;
1951 rc.top += arrow_bitmap_height;
1952 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1954 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1955 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1958 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1959 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1960 MENU_DrawScrollArrows(lppop, hdc);
1962 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1964 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1965 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1966 MENU_DrawScrollArrows(lppop, hdc);
1972 /***********************************************************************
1973 * MENU_SelectItem
1975 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1976 BOOL sendMenuSelect, HMENU topmenu )
1978 LPPOPUPMENU lppop;
1979 HDC hdc;
1981 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1983 lppop = MENU_GetMenu( hmenu );
1984 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1986 if (lppop->FocusedItem == wIndex) return;
1987 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1988 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1989 if (!top_popup) {
1990 top_popup = lppop->hWnd;
1991 top_popup_hmenu = hmenu;
1994 SelectObject( hdc, get_menu_font(FALSE));
1996 /* Clear previous highlighted item */
1997 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1999 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2000 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2001 lppop->Height, !(lppop->wFlags & MF_POPUP),
2002 ODA_SELECT );
2005 /* Highlight new item (if any) */
2006 lppop->FocusedItem = wIndex;
2007 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2009 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2010 lppop->items[wIndex].fState |= MF_HILITE;
2011 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2012 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2013 &lppop->items[wIndex], lppop->Height,
2014 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2016 if (sendMenuSelect)
2018 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2019 SendMessageW( hwndOwner, WM_MENUSELECT,
2020 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2021 ip->fType | ip->fState |
2022 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2025 else if (sendMenuSelect) {
2026 if(topmenu){
2027 int pos;
2028 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2029 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2030 MENUITEM *ip = &ptm->items[pos];
2031 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2032 ip->fType | ip->fState |
2033 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2037 ReleaseDC( lppop->hWnd, hdc );
2041 /***********************************************************************
2042 * MENU_MoveSelection
2044 * Moves currently selected item according to the offset parameter.
2045 * If there is no selection then it should select the last item if
2046 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2048 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2050 INT i;
2051 POPUPMENU *menu;
2053 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2055 menu = MENU_GetMenu( hmenu );
2056 if ((!menu) || (!menu->items)) return;
2058 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2060 if( menu->nItems == 1 ) return; else
2061 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2062 ; i += offset)
2063 if (!(menu->items[i].fType & MF_SEPARATOR))
2065 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2066 return;
2070 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2071 i >= 0 && i < menu->nItems ; i += offset)
2072 if (!(menu->items[i].fType & MF_SEPARATOR))
2074 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2075 return;
2080 /**********************************************************************
2081 * MENU_InsertItem
2083 * Insert (allocate) a new item into a menu.
2085 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2087 MENUITEM *newItems;
2088 POPUPMENU *menu;
2090 if (!(menu = MENU_GetMenu(hMenu)))
2091 return NULL;
2093 /* Find where to insert new item */
2095 if (flags & MF_BYPOSITION) {
2096 if (pos > menu->nItems)
2097 pos = menu->nItems;
2098 } else {
2099 if (!MENU_FindItem( &hMenu, &pos, flags ))
2100 pos = menu->nItems;
2101 else {
2102 if (!(menu = MENU_GetMenu( hMenu )))
2103 return NULL;
2107 /* Make sure that MDI system buttons stay on the right side.
2108 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2109 * regardless of their id.
2111 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2112 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2113 pos--;
2115 TRACE("inserting at %u flags %x\n", pos, flags);
2117 /* Create new items array */
2119 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2120 if (!newItems)
2122 WARN("allocation failed\n" );
2123 return NULL;
2125 if (menu->nItems > 0)
2127 /* Copy the old array into the new one */
2128 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2129 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2130 (menu->nItems-pos)*sizeof(MENUITEM) );
2131 HeapFree( GetProcessHeap(), 0, menu->items );
2133 menu->items = newItems;
2134 menu->nItems++;
2135 memset( &newItems[pos], 0, sizeof(*newItems) );
2136 menu->Height = 0; /* force size recalculate */
2137 return &newItems[pos];
2141 /**********************************************************************
2142 * MENU_ParseResource
2144 * Parse a standard menu resource and add items to the menu.
2145 * Return a pointer to the end of the resource.
2147 * NOTE: flags is equivalent to the mtOption field
2149 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2151 WORD flags, id = 0;
2152 LPCWSTR str;
2153 BOOL end_flag;
2157 flags = GET_WORD(res);
2158 end_flag = flags & MF_END;
2159 /* Remove MF_END because it has the same value as MF_HILITE */
2160 flags &= ~MF_END;
2161 res += sizeof(WORD);
2162 if (!(flags & MF_POPUP))
2164 id = GET_WORD(res);
2165 res += sizeof(WORD);
2167 str = (LPCWSTR)res;
2168 res += (strlenW(str) + 1) * sizeof(WCHAR);
2169 if (flags & MF_POPUP)
2171 HMENU hSubMenu = CreatePopupMenu();
2172 if (!hSubMenu) return NULL;
2173 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2174 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2176 else /* Not a popup */
2178 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2180 } while (!end_flag);
2181 return res;
2185 /**********************************************************************
2186 * MENUEX_ParseResource
2188 * Parse an extended menu resource and add items to the menu.
2189 * Return a pointer to the end of the resource.
2191 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2193 WORD resinfo;
2194 do {
2195 MENUITEMINFOW mii;
2197 mii.cbSize = sizeof(mii);
2198 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2199 mii.fType = GET_DWORD(res);
2200 res += sizeof(DWORD);
2201 mii.fState = GET_DWORD(res);
2202 res += sizeof(DWORD);
2203 mii.wID = GET_DWORD(res);
2204 res += sizeof(DWORD);
2205 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2206 res += sizeof(WORD);
2207 /* Align the text on a word boundary. */
2208 res += (~((UINT_PTR)res - 1)) & 1;
2209 mii.dwTypeData = (LPWSTR) res;
2210 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2211 /* Align the following fields on a dword boundary. */
2212 res += (~((UINT_PTR)res - 1)) & 3;
2214 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2215 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2217 if (resinfo & 1) { /* Pop-up? */
2218 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2219 res += sizeof(DWORD);
2220 mii.hSubMenu = CreatePopupMenu();
2221 if (!mii.hSubMenu)
2222 return NULL;
2223 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2224 DestroyMenu(mii.hSubMenu);
2225 return NULL;
2227 mii.fMask |= MIIM_SUBMENU;
2228 mii.fType |= MF_POPUP;
2230 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2232 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2233 mii.wID, mii.fType);
2234 mii.fType |= MF_SEPARATOR;
2236 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2237 } while (!(resinfo & MF_END));
2238 return res;
2242 /***********************************************************************
2243 * MENU_GetSubPopup
2245 * Return the handle of the selected sub-popup menu (if any).
2247 static HMENU MENU_GetSubPopup( HMENU hmenu )
2249 POPUPMENU *menu;
2250 MENUITEM *item;
2252 menu = MENU_GetMenu( hmenu );
2254 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2256 item = &menu->items[menu->FocusedItem];
2257 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2258 return item->hSubMenu;
2259 return 0;
2263 /***********************************************************************
2264 * MENU_HideSubPopups
2266 * Hide the sub-popup menus of this menu.
2268 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2269 BOOL sendMenuSelect, UINT wFlags )
2271 POPUPMENU *menu = MENU_GetMenu( hmenu );
2273 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2275 if (menu && top_popup)
2277 HMENU hsubmenu;
2278 POPUPMENU *submenu;
2279 MENUITEM *item;
2281 if (menu->FocusedItem != NO_SELECTED_ITEM)
2283 item = &menu->items[menu->FocusedItem];
2284 if (!(item->fType & MF_POPUP) ||
2285 !(item->fState & MF_MOUSESELECT)) return;
2286 item->fState &= ~MF_MOUSESELECT;
2287 hsubmenu = item->hSubMenu;
2288 } else return;
2290 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2291 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2292 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2293 DestroyWindow( submenu->hWnd );
2294 submenu->hWnd = 0;
2296 if (!(wFlags & TPM_NONOTIFY))
2297 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2298 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2303 /***********************************************************************
2304 * MENU_ShowSubPopup
2306 * Display the sub-menu of the selected item of this menu.
2307 * Return the handle of the submenu, or hmenu if no submenu to display.
2309 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2310 BOOL selectFirst, UINT wFlags )
2312 RECT rect;
2313 POPUPMENU *menu;
2314 MENUITEM *item;
2315 HDC hdc;
2317 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2319 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2321 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2323 item = &menu->items[menu->FocusedItem];
2324 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2325 return hmenu;
2327 /* message must be sent before using item,
2328 because nearly everything may be changed by the application ! */
2330 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2331 if (!(wFlags & TPM_NONOTIFY))
2332 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2333 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2335 item = &menu->items[menu->FocusedItem];
2336 rect = item->rect;
2338 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2339 if (!(item->fState & MF_HILITE))
2341 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2342 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2344 SelectObject( hdc, get_menu_font(FALSE));
2346 item->fState |= MF_HILITE;
2347 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2348 ReleaseDC( menu->hWnd, hdc );
2350 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2351 item->rect = rect;
2353 item->fState |= MF_MOUSESELECT;
2355 if (IS_SYSTEM_MENU(menu))
2357 MENU_InitSysMenuPopup(item->hSubMenu,
2358 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2359 GetClassLongW( menu->hWnd, GCL_STYLE));
2361 NC_GetSysPopupPos( menu->hWnd, &rect );
2362 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2363 rect.top = rect.bottom;
2364 rect.right = GetSystemMetrics(SM_CXSIZE);
2365 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2367 else
2369 GetWindowRect( menu->hWnd, &rect );
2370 if (menu->wFlags & MF_POPUP)
2372 RECT rc = item->rect;
2374 MENU_AdjustMenuItemRect(menu, &rc);
2376 /* The first item in the popup menu has to be at the
2377 same y position as the focused menu item */
2378 if (wFlags & TPM_LAYOUTRTL)
2379 rect.left += GetSystemMetrics(SM_CXBORDER);
2380 else
2381 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2382 rect.top += rc.top - MENU_TOP_MARGIN;
2383 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2384 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2385 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2387 else
2389 if (wFlags & TPM_LAYOUTRTL)
2390 rect.left = rect.right - item->rect.left;
2391 else
2392 rect.left += item->rect.left;
2393 rect.top += item->rect.bottom;
2394 rect.right = item->rect.right - item->rect.left;
2395 rect.bottom = item->rect.bottom - item->rect.top;
2399 /* use default alignment for submenus */
2400 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2402 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2404 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2405 rect.left, rect.top, rect.right, rect.bottom );
2406 if (selectFirst)
2407 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2408 return item->hSubMenu;
2413 /**********************************************************************
2414 * MENU_IsMenuActive
2416 HWND MENU_IsMenuActive(void)
2418 return top_popup;
2421 /**********************************************************************
2422 * MENU_EndMenu
2424 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2426 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2428 void MENU_EndMenu( HWND hwnd )
2430 POPUPMENU *menu;
2431 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2432 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2435 /***********************************************************************
2436 * MENU_PtMenu
2438 * Walks menu chain trying to find a menu pt maps to.
2440 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2442 POPUPMENU *menu = MENU_GetMenu( hMenu );
2443 UINT item = menu->FocusedItem;
2444 HMENU ret;
2446 /* try subpopup first (if any) */
2447 ret = (item != NO_SELECTED_ITEM &&
2448 (menu->items[item].fType & MF_POPUP) &&
2449 (menu->items[item].fState & MF_MOUSESELECT))
2450 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2452 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2454 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2455 if( menu->wFlags & MF_POPUP )
2457 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2459 else if (ht == HTSYSMENU)
2460 ret = get_win_sys_menu( menu->hWnd );
2461 else if (ht == HTMENU)
2462 ret = GetMenu( menu->hWnd );
2464 return ret;
2467 /***********************************************************************
2468 * MENU_ExecFocusedItem
2470 * Execute a menu item (for instance when user pressed Enter).
2471 * Return the wID of the executed item. Otherwise, -1 indicating
2472 * that no menu item was executed, -2 if a popup is shown;
2473 * Have to receive the flags for the TrackPopupMenu options to avoid
2474 * sending unwanted message.
2477 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2479 MENUITEM *item;
2480 POPUPMENU *menu = MENU_GetMenu( hMenu );
2482 TRACE("%p hmenu=%p\n", pmt, hMenu);
2484 if (!menu || !menu->nItems ||
2485 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2487 item = &menu->items[menu->FocusedItem];
2489 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2491 if (!(item->fType & MF_POPUP))
2493 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2495 /* If TPM_RETURNCMD is set you return the id, but
2496 do not send a message to the owner */
2497 if(!(wFlags & TPM_RETURNCMD))
2499 if( menu->wFlags & MF_SYSMENU )
2500 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2501 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2502 else
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2505 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2507 if (dwStyle & MNS_NOTIFYBYPOS)
2508 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2509 (LPARAM)hMenu);
2510 else
2511 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2514 return item->wID;
2517 else
2519 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2520 return -2;
2523 return -1;
2526 /***********************************************************************
2527 * MENU_SwitchTracking
2529 * Helper function for menu navigation routines.
2531 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2536 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2538 if( pmt->hTopMenu != hPtMenu &&
2539 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2541 /* both are top level menus (system and menu-bar) */
2542 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2543 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2544 pmt->hTopMenu = hPtMenu;
2546 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2547 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2551 /***********************************************************************
2552 * MENU_ButtonDown
2554 * Return TRUE if we can go on with menu tracking.
2556 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2558 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2560 if (hPtMenu)
2562 UINT id = 0;
2563 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2564 MENUITEM *item;
2566 if( IS_SYSTEM_MENU(ptmenu) )
2567 item = ptmenu->items;
2568 else
2569 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2571 if( item )
2573 if( ptmenu->FocusedItem != id )
2574 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2576 /* If the popup menu is not already "popped" */
2577 if(!(item->fState & MF_MOUSESELECT ))
2579 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2582 return TRUE;
2584 /* Else the click was on the menu bar, finish the tracking */
2586 return FALSE;
2589 /***********************************************************************
2590 * MENU_ButtonUp
2592 * Return the value of MENU_ExecFocusedItem if
2593 * the selected item was not a popup. Else open the popup.
2594 * A -1 return value indicates that we go on with menu tracking.
2597 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2599 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2601 if (hPtMenu)
2603 UINT id = 0;
2604 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2605 MENUITEM *item;
2607 if( IS_SYSTEM_MENU(ptmenu) )
2608 item = ptmenu->items;
2609 else
2610 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2612 if( item && (ptmenu->FocusedItem == id ))
2614 debug_print_menuitem ("FocusedItem: ", item, "");
2616 if( !(item->fType & MF_POPUP) )
2618 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2619 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2620 return executedMenuId;
2623 /* If we are dealing with the menu bar */
2624 /* and this is a click on an already "popped" item: */
2625 /* Stop the menu tracking and close the opened submenus */
2626 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2627 return 0;
2629 if( GetMenu(ptmenu->hWnd) == hPtMenu )
2630 ptmenu->bTimeToHide = TRUE;
2632 return -1;
2636 /***********************************************************************
2637 * MENU_MouseMove
2639 * Return TRUE if we can go on with menu tracking.
2641 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2643 UINT id = NO_SELECTED_ITEM;
2644 POPUPMENU *ptmenu = NULL;
2646 if( hPtMenu )
2648 ptmenu = MENU_GetMenu( hPtMenu );
2649 if( IS_SYSTEM_MENU(ptmenu) )
2650 id = 0;
2651 else
2652 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2655 if( id == NO_SELECTED_ITEM )
2657 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2658 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2661 else if( ptmenu->FocusedItem != id )
2663 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2664 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2666 return TRUE;
2670 /***********************************************************************
2671 * MENU_DoNextMenu
2673 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2675 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2677 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2678 BOOL atEnd = FALSE;
2680 /* When skipping left, we need to do something special after the
2681 first menu. */
2682 if (vk == VK_LEFT && menu->FocusedItem == 0)
2684 atEnd = TRUE;
2686 /* When skipping right, for the non-system menu, we need to
2687 handle the last non-special menu item (ie skip any window
2688 icons such as MDI maximize, restore or close) */
2689 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2691 UINT i = menu->FocusedItem + 1;
2692 while (i < menu->nItems) {
2693 if ((menu->items[i].wID >= SC_SIZE &&
2694 menu->items[i].wID <= SC_RESTORE)) {
2695 i++;
2696 } else break;
2698 if (i == menu->nItems) {
2699 atEnd = TRUE;
2702 /* When skipping right, we need to cater for the system menu */
2703 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2705 if (menu->FocusedItem == (menu->nItems - 1)) {
2706 atEnd = TRUE;
2710 if( atEnd )
2712 MDINEXTMENU next_menu;
2713 HMENU hNewMenu;
2714 HWND hNewWnd;
2715 UINT id = 0;
2717 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2718 next_menu.hmenuNext = 0;
2719 next_menu.hwndNext = 0;
2720 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2722 TRACE("%p [%p] -> %p [%p]\n",
2723 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2725 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2727 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2728 hNewWnd = pmt->hOwnerWnd;
2729 if( IS_SYSTEM_MENU(menu) )
2731 /* switch to the menu bar */
2733 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2735 if( vk == VK_LEFT )
2737 menu = MENU_GetMenu( hNewMenu );
2738 id = menu->nItems - 1;
2740 /* Skip backwards over any system predefined icons,
2741 eg. MDI close, restore etc icons */
2742 while ((id > 0) &&
2743 (menu->items[id].wID >= SC_SIZE &&
2744 menu->items[id].wID <= SC_RESTORE)) id--;
2747 else if (style & WS_SYSMENU )
2749 /* switch to the system menu */
2750 hNewMenu = get_win_sys_menu( hNewWnd );
2752 else return FALSE;
2754 else /* application returned a new menu to switch to */
2756 hNewMenu = next_menu.hmenuNext;
2757 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2759 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2761 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2763 if (style & WS_SYSMENU &&
2764 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2766 /* get the real system menu */
2767 hNewMenu = get_win_sys_menu(hNewWnd);
2769 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2771 /* FIXME: Not sure what to do here;
2772 * perhaps try to track hNewMenu as a popup? */
2774 TRACE(" -- got confused.\n");
2775 return FALSE;
2778 else return FALSE;
2781 if( hNewMenu != pmt->hTopMenu )
2783 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2784 FALSE, 0 );
2785 if( pmt->hCurrentMenu != pmt->hTopMenu )
2786 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2789 if( hNewWnd != pmt->hOwnerWnd )
2791 pmt->hOwnerWnd = hNewWnd;
2792 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2795 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2796 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2798 return TRUE;
2800 return FALSE;
2803 /***********************************************************************
2804 * MENU_SuspendPopup
2806 * The idea is not to show the popup if the next input message is
2807 * going to hide it anyway.
2809 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2811 MSG msg;
2813 msg.hwnd = pmt->hOwnerWnd;
2815 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2816 pmt->trackFlags |= TF_SKIPREMOVE;
2818 switch( uMsg )
2820 case WM_KEYDOWN:
2821 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2822 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2824 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2825 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2826 if( msg.message == WM_KEYDOWN &&
2827 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2829 pmt->trackFlags |= TF_SUSPENDPOPUP;
2830 return TRUE;
2833 break;
2836 /* failures go through this */
2837 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2838 return FALSE;
2841 /***********************************************************************
2842 * MENU_KeyEscape
2844 * Handle a VK_ESCAPE key event in a menu.
2846 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2848 BOOL bEndMenu = TRUE;
2850 if (pmt->hCurrentMenu != pmt->hTopMenu)
2852 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2854 if (menu->wFlags & MF_POPUP)
2856 HMENU hmenutmp, hmenuprev;
2858 hmenuprev = hmenutmp = pmt->hTopMenu;
2860 /* close topmost popup */
2861 while (hmenutmp != pmt->hCurrentMenu)
2863 hmenuprev = hmenutmp;
2864 hmenutmp = MENU_GetSubPopup( hmenuprev );
2867 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2868 pmt->hCurrentMenu = hmenuprev;
2869 bEndMenu = FALSE;
2873 return bEndMenu;
2876 /***********************************************************************
2877 * MENU_KeyLeft
2879 * Handle a VK_LEFT key event in a menu.
2881 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2883 POPUPMENU *menu;
2884 HMENU hmenutmp, hmenuprev;
2885 UINT prevcol;
2887 hmenuprev = hmenutmp = pmt->hTopMenu;
2888 menu = MENU_GetMenu( hmenutmp );
2890 /* Try to move 1 column left (if possible) */
2891 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2892 NO_SELECTED_ITEM ) {
2894 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2895 prevcol, TRUE, 0 );
2896 return;
2899 /* close topmost popup */
2900 while (hmenutmp != pmt->hCurrentMenu)
2902 hmenuprev = hmenutmp;
2903 hmenutmp = MENU_GetSubPopup( hmenuprev );
2906 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2907 pmt->hCurrentMenu = hmenuprev;
2909 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2911 /* move menu bar selection if no more popups are left */
2913 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2914 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2916 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2918 /* A sublevel menu was displayed - display the next one
2919 * unless there is another displacement coming up */
2921 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2922 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2923 pmt->hTopMenu, TRUE, wFlags);
2929 /***********************************************************************
2930 * MENU_KeyRight
2932 * Handle a VK_RIGHT key event in a menu.
2934 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2936 HMENU hmenutmp;
2937 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2938 UINT nextcol;
2940 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2941 pmt->hCurrentMenu,
2942 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2943 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2945 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2947 /* If already displaying a popup, try to display sub-popup */
2949 hmenutmp = pmt->hCurrentMenu;
2950 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2952 /* if subpopup was displayed then we are done */
2953 if (hmenutmp != pmt->hCurrentMenu) return;
2956 /* Check to see if there's another column */
2957 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2958 NO_SELECTED_ITEM ) {
2959 TRACE("Going to %d.\n", nextcol );
2960 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2961 nextcol, TRUE, 0 );
2962 return;
2965 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2967 if( pmt->hCurrentMenu != pmt->hTopMenu )
2969 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2970 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2971 } else hmenutmp = 0;
2973 /* try to move to the next item */
2974 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2975 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2977 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2978 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2979 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2980 pmt->hTopMenu, TRUE, wFlags);
2984 static void CALLBACK release_capture( BOOL __normal )
2986 set_capture_window( 0, GUI_INMENUMODE, NULL );
2989 /***********************************************************************
2990 * MENU_TrackMenu
2992 * Menu tracking code.
2994 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2995 HWND hwnd, const RECT *lprect )
2997 MSG msg;
2998 POPUPMENU *menu;
2999 BOOL fRemove;
3000 INT executedMenuId = -1;
3001 MTRACKER mt;
3002 BOOL enterIdleSent = FALSE;
3003 HWND capture_win;
3005 mt.trackFlags = 0;
3006 mt.hCurrentMenu = hmenu;
3007 mt.hTopMenu = hmenu;
3008 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3009 mt.pt.x = x;
3010 mt.pt.y = y;
3012 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3013 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3015 if (!(menu = MENU_GetMenu( hmenu )))
3017 WARN("Invalid menu handle %p\n", hmenu);
3018 SetLastError(ERROR_INVALID_MENU_HANDLE);
3019 return FALSE;
3022 if (wFlags & TPM_BUTTONDOWN)
3024 /* Get the result in order to start the tracking or not */
3025 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3026 fEndMenu = !fRemove;
3029 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3031 /* owner may not be visible when tracking a popup, so use the menu itself */
3032 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3033 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3035 if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
3036 return FALSE;
3038 __TRY while (!fEndMenu)
3040 menu = MENU_GetMenu( mt.hCurrentMenu );
3041 if (!menu) /* sometimes happens if I do a window manager close */
3042 break;
3044 /* we have to keep the message in the queue until it's
3045 * clear that menu loop is not over yet. */
3047 for (;;)
3049 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3051 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3052 /* remove the message from the queue */
3053 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3055 else
3057 if (!enterIdleSent)
3059 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3060 enterIdleSent = TRUE;
3061 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3063 WaitMessage();
3067 /* check if EndMenu() tried to cancel us, by posting this message */
3068 if(msg.message == WM_CANCELMODE)
3070 /* we are now out of the loop */
3071 fEndMenu = TRUE;
3073 /* remove the message from the queue */
3074 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3076 /* break out of internal loop, ala ESCAPE */
3077 break;
3080 TranslateMessage( &msg );
3081 mt.pt = msg.pt;
3083 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3084 enterIdleSent=FALSE;
3086 fRemove = FALSE;
3087 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3090 * Use the mouse coordinates in lParam instead of those in the MSG
3091 * struct to properly handle synthetic messages. They are already
3092 * in screen coordinates.
3094 mt.pt.x = (short)LOWORD(msg.lParam);
3095 mt.pt.y = (short)HIWORD(msg.lParam);
3097 /* Find a menu for this mouse event */
3098 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3100 switch(msg.message)
3102 /* no WM_NC... messages in captured state */
3104 case WM_RBUTTONDBLCLK:
3105 case WM_RBUTTONDOWN:
3106 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3107 /* fall through */
3108 case WM_LBUTTONDBLCLK:
3109 case WM_LBUTTONDOWN:
3110 /* If the message belongs to the menu, removes it from the queue */
3111 /* Else, end menu tracking */
3112 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3113 fEndMenu = !fRemove;
3114 break;
3116 case WM_RBUTTONUP:
3117 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3118 /* fall through */
3119 case WM_LBUTTONUP:
3120 /* Check if a menu was selected by the mouse */
3121 if (hmenu)
3123 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3124 TRACE("executedMenuId %d\n", executedMenuId);
3126 /* End the loop if executedMenuId is an item ID */
3127 /* or if the job was done (executedMenuId = 0). */
3128 fEndMenu = fRemove = (executedMenuId != -1);
3130 /* No menu was selected by the mouse */
3131 /* if the function was called by TrackPopupMenu, continue
3132 with the menu tracking. If not, stop it */
3133 else
3134 fEndMenu = !(wFlags & TPM_POPUPMENU);
3136 break;
3138 case WM_MOUSEMOVE:
3139 /* the selected menu item must be changed every time */
3140 /* the mouse moves. */
3142 if (hmenu)
3143 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3145 } /* switch(msg.message) - mouse */
3147 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3149 fRemove = TRUE; /* Keyboard messages are always removed */
3150 switch(msg.message)
3152 case WM_KEYDOWN:
3153 case WM_SYSKEYDOWN:
3154 switch(msg.wParam)
3156 case VK_MENU:
3157 case VK_F10:
3158 fEndMenu = TRUE;
3159 break;
3161 case VK_HOME:
3162 case VK_END:
3163 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3164 NO_SELECTED_ITEM, FALSE, 0 );
3165 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3166 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3167 break;
3169 case VK_UP:
3170 case VK_DOWN: /* If on menu bar, pull-down the menu */
3172 menu = MENU_GetMenu( mt.hCurrentMenu );
3173 if (!(menu->wFlags & MF_POPUP))
3174 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3175 else /* otherwise try to move selection */
3176 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3177 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3178 break;
3180 case VK_LEFT:
3181 MENU_KeyLeft( &mt, wFlags );
3182 break;
3184 case VK_RIGHT:
3185 MENU_KeyRight( &mt, wFlags );
3186 break;
3188 case VK_ESCAPE:
3189 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3190 break;
3192 case VK_F1:
3194 HELPINFO hi;
3195 hi.cbSize = sizeof(HELPINFO);
3196 hi.iContextType = HELPINFO_MENUITEM;
3197 if (menu->FocusedItem == NO_SELECTED_ITEM)
3198 hi.iCtrlId = 0;
3199 else
3200 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3201 hi.hItemHandle = hmenu;
3202 hi.dwContextId = menu->dwContextHelpID;
3203 hi.MousePos = msg.pt;
3204 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3205 break;
3208 default:
3209 break;
3211 break; /* WM_KEYDOWN */
3213 case WM_CHAR:
3214 case WM_SYSCHAR:
3216 UINT pos;
3218 if (msg.wParam == '\r' || msg.wParam == ' ')
3220 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3221 fEndMenu = (executedMenuId != -2);
3223 break;
3226 /* Hack to avoid control chars. */
3227 /* We will find a better way real soon... */
3228 if (msg.wParam < 32) break;
3230 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3231 LOWORD(msg.wParam), FALSE );
3232 if (pos == (UINT)-2) fEndMenu = TRUE;
3233 else if (pos == (UINT)-1) MessageBeep(0);
3234 else
3236 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3237 TRUE, 0 );
3238 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3239 fEndMenu = (executedMenuId != -2);
3242 break;
3243 } /* switch(msg.message) - kbd */
3245 else
3247 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3248 DispatchMessageW( &msg );
3249 continue;
3252 if (!fEndMenu) fRemove = TRUE;
3254 /* finally remove message from the queue */
3256 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3257 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3258 else mt.trackFlags &= ~TF_SKIPREMOVE;
3260 __FINALLY( release_capture )
3262 /* If dropdown is still painted and the close box is clicked on
3263 then the menu will be destroyed as part of the DispatchMessage above.
3264 This will then invalidate the menu handle in mt.hTopMenu. We should
3265 check for this first. */
3266 if( IsMenu( mt.hTopMenu ) )
3268 menu = MENU_GetMenu( mt.hTopMenu );
3270 if( IsWindow( mt.hOwnerWnd ) )
3272 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3274 if (menu && (menu->wFlags & MF_POPUP))
3276 DestroyWindow( menu->hWnd );
3277 menu->hWnd = 0;
3279 if (!(wFlags & TPM_NONOTIFY))
3280 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3281 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3283 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3284 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3287 /* Reset the variable for hiding menu */
3288 if( menu ) menu->bTimeToHide = FALSE;
3291 /* The return value is only used by TrackPopupMenu */
3292 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3293 if (executedMenuId == -1) executedMenuId = 0;
3294 return executedMenuId;
3297 /***********************************************************************
3298 * MENU_InitTracking
3300 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3302 POPUPMENU *menu;
3304 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3306 HideCaret(0);
3308 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3310 /* This makes the menus of applications built with Delphi work.
3311 * It also enables menus to be displayed in more than one window,
3312 * but there are some bugs left that need to be fixed in this case.
3314 if (!bPopup) menu->hWnd = hWnd;
3315 if (!top_popup)
3317 top_popup = menu->hWnd;
3318 top_popup_hmenu = hMenu;
3321 fEndMenu = FALSE;
3323 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3324 if (!(wFlags & TPM_NONOTIFY))
3325 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3327 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3329 if (!(wFlags & TPM_NONOTIFY))
3331 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3332 /* If an app changed/recreated menu bar entries in WM_INITMENU
3333 * menu sizes will be recalculated once the menu created/shown.
3337 return TRUE;
3340 /***********************************************************************
3341 * MENU_ExitTracking
3343 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3345 TRACE("hwnd=%p\n", hWnd);
3347 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3348 ShowCaret(0);
3349 top_popup = 0;
3350 top_popup_hmenu = NULL;
3351 return TRUE;
3354 /***********************************************************************
3355 * MENU_TrackMouseMenuBar
3357 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3359 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3361 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3362 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3364 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3366 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3367 if (IsMenu(hMenu))
3369 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3371 /* fetch the window menu again, it may have changed */
3372 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3373 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3374 MENU_ExitTracking(hWnd, FALSE);
3379 /***********************************************************************
3380 * MENU_TrackKbdMenuBar
3382 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3384 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3386 UINT uItem = NO_SELECTED_ITEM;
3387 HMENU hTrackMenu;
3388 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3390 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3392 /* find window that has a menu */
3394 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3395 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3397 /* check if we have to track a system menu */
3399 hTrackMenu = GetMenu( hwnd );
3400 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3402 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3403 hTrackMenu = get_win_sys_menu( hwnd );
3404 uItem = 0;
3405 wParam |= HTSYSMENU; /* prevent item lookup */
3407 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3409 if (!IsMenu( hTrackMenu )) return;
3411 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3413 /* fetch the window menu again, it may have changed */
3414 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3416 if( wChar && wChar != ' ' )
3418 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3419 if ( uItem >= (UINT)(-2) )
3421 if( uItem == (UINT)(-1) ) MessageBeep(0);
3422 /* schedule end of menu tracking */
3423 wFlags |= TF_ENDMENU;
3424 goto track_menu;
3428 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3430 if (!(wParam & HTSYSMENU) || wChar == ' ')
3432 if( uItem == NO_SELECTED_ITEM )
3433 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3434 else
3435 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3438 track_menu:
3439 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3440 MENU_ExitTracking( hwnd, FALSE );
3443 /**********************************************************************
3444 * TrackPopupMenuEx (USER32.@)
3446 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3447 HWND hWnd, LPTPMPARAMS lpTpm )
3449 POPUPMENU *menu;
3450 BOOL ret = FALSE;
3452 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3453 hMenu, wFlags, x, y, hWnd, lpTpm,
3454 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3456 /* Parameter check */
3457 /* FIXME: this check is performed several times, here and in the called
3458 functions. That could be optimized */
3459 if (!(menu = MENU_GetMenu( hMenu )))
3461 SetLastError( ERROR_INVALID_MENU_HANDLE );
3462 return FALSE;
3465 if (IsWindow(menu->hWnd))
3467 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3468 return FALSE;
3471 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3473 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3475 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3476 if (!(wFlags & TPM_NONOTIFY))
3477 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3479 if (menu->wFlags & MF_SYSMENU)
3480 MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
3481 GetClassLongW( hWnd, GCL_STYLE));
3483 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3484 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3485 lpTpm ? &lpTpm->rcExclude : NULL );
3486 MENU_ExitTracking(hWnd, TRUE);
3488 if (menu->hWnd)
3490 DestroyWindow( menu->hWnd );
3491 menu->hWnd = 0;
3493 if (!(wFlags & TPM_NONOTIFY))
3494 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3495 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3499 return ret;
3502 /**********************************************************************
3503 * TrackPopupMenu (USER32.@)
3505 * Like the win32 API, the function return the command ID only if the
3506 * flag TPM_RETURNCMD is on.
3509 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3510 INT nReserved, HWND hWnd, const RECT *lpRect )
3512 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3515 /***********************************************************************
3516 * PopupMenuWndProc
3518 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3520 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3522 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3524 switch(message)
3526 case WM_CREATE:
3528 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3529 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3530 return 0;
3533 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3534 return MA_NOACTIVATE;
3536 case WM_PAINT:
3538 PAINTSTRUCT ps;
3539 BeginPaint( hwnd, &ps );
3540 MENU_DrawPopupMenu( hwnd, ps.hdc,
3541 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3542 EndPaint( hwnd, &ps );
3543 return 0;
3546 case WM_PRINTCLIENT:
3548 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3549 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3550 return 0;
3553 case WM_ERASEBKGND:
3554 return 1;
3556 case WM_DESTROY:
3557 /* zero out global pointer in case resident popup window was destroyed. */
3558 if (hwnd == top_popup) {
3559 top_popup = 0;
3560 top_popup_hmenu = NULL;
3562 break;
3564 case WM_SHOWWINDOW:
3566 if( wParam )
3568 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3570 else
3571 SetWindowLongPtrW( hwnd, 0, 0 );
3572 break;
3574 case MN_GETHMENU:
3575 return GetWindowLongPtrW( hwnd, 0 );
3577 default:
3578 return DefWindowProcW( hwnd, message, wParam, lParam );
3580 return 0;
3584 /***********************************************************************
3585 * MENU_GetMenuBarHeight
3587 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3589 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3590 INT orgX, INT orgY )
3592 HDC hdc;
3593 RECT rectBar;
3594 LPPOPUPMENU lppop;
3596 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3598 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3600 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3601 SelectObject( hdc, get_menu_font(FALSE));
3602 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3603 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3604 ReleaseDC( hwnd, hdc );
3605 return lppop->Height;
3609 /*******************************************************************
3610 * ChangeMenuA (USER32.@)
3612 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3613 UINT id, UINT flags )
3615 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3616 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3617 id, data );
3618 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3619 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3620 id, data );
3621 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3622 flags & MF_BYPOSITION ? pos : id,
3623 flags & ~MF_REMOVE );
3624 /* Default: MF_INSERT */
3625 return InsertMenuA( hMenu, pos, flags, id, data );
3629 /*******************************************************************
3630 * ChangeMenuW (USER32.@)
3632 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3633 UINT id, UINT flags )
3635 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3636 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3637 id, data );
3638 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3639 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3640 id, data );
3641 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3642 flags & MF_BYPOSITION ? pos : id,
3643 flags & ~MF_REMOVE );
3644 /* Default: MF_INSERT */
3645 return InsertMenuW( hMenu, pos, flags, id, data );
3649 /*******************************************************************
3650 * CheckMenuItem (USER32.@)
3652 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3654 MENUITEM *item;
3655 DWORD ret;
3657 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3658 ret = item->fState & MF_CHECKED;
3659 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3660 else item->fState &= ~MF_CHECKED;
3661 return ret;
3665 /**********************************************************************
3666 * EnableMenuItem (USER32.@)
3668 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3670 UINT oldflags;
3671 MENUITEM *item;
3672 POPUPMENU *menu;
3674 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3676 /* Get the Popupmenu to access the owner menu */
3677 if (!(menu = MENU_GetMenu(hMenu)))
3678 return (UINT)-1;
3680 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3681 return (UINT)-1;
3683 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3684 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3686 /* If the close item in the system menu change update the close button */
3687 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3689 if (menu->hSysMenuOwner != 0)
3691 RECT rc;
3692 POPUPMENU* parentMenu;
3694 /* Get the parent menu to access*/
3695 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3696 return (UINT)-1;
3698 /* Refresh the frame to reflect the change */
3699 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3700 rc.bottom = 0;
3701 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3705 return oldflags;
3709 /*******************************************************************
3710 * GetMenuStringA (USER32.@)
3712 INT WINAPI GetMenuStringA(
3713 HMENU hMenu, /* [in] menuhandle */
3714 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3715 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3716 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3717 UINT wFlags /* [in] MF_ flags */
3719 MENUITEM *item;
3721 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3722 if (str && nMaxSiz) str[0] = '\0';
3723 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3724 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3725 return 0;
3727 if (!item->text) return 0;
3728 if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3729 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3730 str[nMaxSiz-1] = 0;
3731 TRACE("returning %s\n", debugstr_a(str));
3732 return strlen(str);
3736 /*******************************************************************
3737 * GetMenuStringW (USER32.@)
3739 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3740 LPWSTR str, INT nMaxSiz, UINT wFlags )
3742 MENUITEM *item;
3744 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3745 if (str && nMaxSiz) str[0] = '\0';
3746 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3747 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3748 return 0;
3750 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3751 if( !(item->text)) {
3752 str[0] = 0;
3753 return 0;
3755 lstrcpynW( str, item->text, nMaxSiz );
3756 TRACE("returning %s\n", debugstr_w(str));
3757 return strlenW(str);
3761 /**********************************************************************
3762 * HiliteMenuItem (USER32.@)
3764 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3765 UINT wHilite )
3767 LPPOPUPMENU menu;
3768 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3769 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3770 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3771 if (menu->FocusedItem == wItemID) return TRUE;
3772 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3773 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3774 return TRUE;
3778 /**********************************************************************
3779 * GetMenuState (USER32.@)
3781 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3783 MENUITEM *item;
3784 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3785 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3786 debug_print_menuitem (" item: ", item, "");
3787 if (item->fType & MF_POPUP)
3789 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3790 if (!menu) return -1;
3791 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3793 else
3795 /* We used to (from way back then) mask the result to 0xff. */
3796 /* I don't know why and it seems wrong as the documented */
3797 /* return flag MF_SEPARATOR is outside that mask. */
3798 return (item->fType | item->fState);
3803 /**********************************************************************
3804 * GetMenuItemCount (USER32.@)
3806 INT WINAPI GetMenuItemCount( HMENU hMenu )
3808 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3809 if (!menu) return -1;
3810 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3811 return menu->nItems;
3815 /**********************************************************************
3816 * GetMenuItemID (USER32.@)
3818 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3820 MENUITEM * lpmi;
3822 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3823 if (lpmi->fType & MF_POPUP) return -1;
3824 return lpmi->wID;
3829 /**********************************************************************
3830 * MENU_mnu2mnuii
3832 * Uses flags, id and text ptr, passed by InsertMenu() and
3833 * ModifyMenu() to setup a MenuItemInfo structure.
3835 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3836 LPMENUITEMINFOW pmii)
3838 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3839 pmii->cbSize = sizeof( MENUITEMINFOW);
3840 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3841 /* setting bitmap clears text and vice versa */
3842 if( IS_STRING_ITEM(flags)) {
3843 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3844 if( !str)
3845 flags |= MF_SEPARATOR;
3846 /* Item beginning with a backspace is a help item */
3847 /* FIXME: wrong place, this is only true in win16 */
3848 else if( *str == '\b') {
3849 flags |= MF_HELP;
3850 str++;
3852 pmii->dwTypeData = (LPWSTR)str;
3853 } else if( flags & MFT_BITMAP){
3854 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3855 pmii->hbmpItem = (HBITMAP)str;
3857 if( flags & MF_OWNERDRAW){
3858 pmii->fMask |= MIIM_DATA;
3859 pmii->dwItemData = (ULONG_PTR) str;
3861 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3862 pmii->fMask |= MIIM_SUBMENU;
3863 pmii->hSubMenu = (HMENU)id;
3865 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3866 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3867 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3868 pmii->wID = (UINT)id;
3872 /*******************************************************************
3873 * InsertMenuW (USER32.@)
3875 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3876 UINT_PTR id, LPCWSTR str )
3878 MENUITEM *item;
3879 MENUITEMINFOW mii;
3881 if (IS_STRING_ITEM(flags) && str)
3882 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3883 hMenu, pos, flags, id, debugstr_w(str) );
3884 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3885 hMenu, pos, flags, id, str );
3887 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3888 MENU_mnu2mnuii( flags, id, str, &mii);
3889 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3891 RemoveMenu( hMenu, pos, flags );
3892 return FALSE;
3895 item->hCheckBit = item->hUnCheckBit = 0;
3896 return TRUE;
3900 /*******************************************************************
3901 * InsertMenuA (USER32.@)
3903 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3904 UINT_PTR id, LPCSTR str )
3906 BOOL ret = FALSE;
3908 if (IS_STRING_ITEM(flags) && str)
3910 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3911 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3912 if (newstr)
3914 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3915 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3916 HeapFree( GetProcessHeap(), 0, newstr );
3918 return ret;
3920 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3924 /*******************************************************************
3925 * AppendMenuA (USER32.@)
3927 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3928 UINT_PTR id, LPCSTR data )
3930 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3934 /*******************************************************************
3935 * AppendMenuW (USER32.@)
3937 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3938 UINT_PTR id, LPCWSTR data )
3940 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3944 /**********************************************************************
3945 * RemoveMenu (USER32.@)
3947 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3949 LPPOPUPMENU menu;
3950 MENUITEM *item;
3952 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3953 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3954 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3956 /* Remove item */
3958 MENU_FreeItemData( item );
3960 if (--menu->nItems == 0)
3962 HeapFree( GetProcessHeap(), 0, menu->items );
3963 menu->items = NULL;
3965 else
3967 while(nPos < menu->nItems)
3969 *item = *(item+1);
3970 item++;
3971 nPos++;
3973 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3974 menu->nItems * sizeof(MENUITEM) );
3976 return TRUE;
3980 /**********************************************************************
3981 * DeleteMenu (USER32.@)
3983 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3985 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3986 if (!item) return FALSE;
3987 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3988 /* nPos is now the position of the item */
3989 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3990 return TRUE;
3994 /*******************************************************************
3995 * ModifyMenuW (USER32.@)
3997 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3998 UINT_PTR id, LPCWSTR str )
4000 MENUITEM *item;
4001 MENUITEMINFOW mii;
4003 if (IS_STRING_ITEM(flags))
4004 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
4005 else
4006 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
4008 if (!(item = MENU_FindItem( &hMenu, &pos, flags )))
4010 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4011 if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
4012 return FALSE;
4014 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4015 MENU_mnu2mnuii( flags, id, str, &mii);
4016 return SetMenuItemInfo_common( item, &mii, TRUE);
4020 /*******************************************************************
4021 * ModifyMenuA (USER32.@)
4023 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4024 UINT_PTR id, LPCSTR str )
4026 BOOL ret = FALSE;
4028 if (IS_STRING_ITEM(flags) && str)
4030 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4031 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
4032 if (newstr)
4034 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
4035 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
4036 HeapFree( GetProcessHeap(), 0, newstr );
4038 return ret;
4040 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4044 /**********************************************************************
4045 * CreatePopupMenu (USER32.@)
4047 HMENU WINAPI CreatePopupMenu(void)
4049 HMENU hmenu;
4050 POPUPMENU *menu;
4052 if (!(hmenu = CreateMenu())) return 0;
4053 menu = MENU_GetMenu( hmenu );
4054 menu->wFlags |= MF_POPUP;
4055 menu->bTimeToHide = FALSE;
4056 return hmenu;
4060 /**********************************************************************
4061 * GetMenuCheckMarkDimensions (USER.417)
4062 * GetMenuCheckMarkDimensions (USER32.@)
4064 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4066 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4070 /**********************************************************************
4071 * SetMenuItemBitmaps (USER32.@)
4073 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4074 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4076 MENUITEM *item;
4078 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4080 if (!hNewCheck && !hNewUnCheck)
4082 item->fState &= ~MF_USECHECKBITMAPS;
4084 else /* Install new bitmaps */
4086 item->hCheckBit = hNewCheck;
4087 item->hUnCheckBit = hNewUnCheck;
4088 item->fState |= MF_USECHECKBITMAPS;
4090 return TRUE;
4094 /**********************************************************************
4095 * CreateMenu (USER32.@)
4097 HMENU WINAPI CreateMenu(void)
4099 HMENU hMenu;
4100 LPPOPUPMENU menu;
4102 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4103 menu->FocusedItem = NO_SELECTED_ITEM;
4104 menu->bTimeToHide = FALSE;
4106 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4108 TRACE("return %p\n", hMenu );
4110 return hMenu;
4114 /**********************************************************************
4115 * DestroyMenu (USER32.@)
4117 BOOL WINAPI DestroyMenu( HMENU hMenu )
4119 LPPOPUPMENU lppop;
4121 TRACE("(%p)\n", hMenu);
4123 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4124 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4126 /* DestroyMenu should not destroy system menu popup owner */
4127 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4129 DestroyWindow( lppop->hWnd );
4130 lppop->hWnd = 0;
4133 if (lppop->items) /* recursively destroy submenus */
4135 int i;
4136 MENUITEM *item = lppop->items;
4137 for (i = lppop->nItems; i > 0; i--, item++)
4139 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4140 MENU_FreeItemData( item );
4142 HeapFree( GetProcessHeap(), 0, lppop->items );
4144 HeapFree( GetProcessHeap(), 0, lppop );
4145 return TRUE;
4149 /**********************************************************************
4150 * GetSystemMenu (USER32.@)
4152 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4154 WND *wndPtr = WIN_GetPtr( hWnd );
4155 HMENU retvalue = 0;
4157 if (wndPtr == WND_DESKTOP) return 0;
4158 if (wndPtr == WND_OTHER_PROCESS)
4160 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4162 else if (wndPtr)
4164 if (wndPtr->hSysMenu && bRevert)
4166 DestroyMenu(wndPtr->hSysMenu);
4167 wndPtr->hSysMenu = 0;
4170 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4171 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4173 if( wndPtr->hSysMenu )
4175 POPUPMENU *menu;
4176 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4178 /* Store the dummy sysmenu handle to facilitate the refresh */
4179 /* of the close button if the SC_CLOSE item change */
4180 menu = MENU_GetMenu(retvalue);
4181 if ( menu )
4182 menu->hSysMenuOwner = wndPtr->hSysMenu;
4184 WIN_ReleasePtr( wndPtr );
4186 return bRevert ? 0 : retvalue;
4190 /*******************************************************************
4191 * SetSystemMenu (USER32.@)
4193 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4195 WND *wndPtr = WIN_GetPtr( hwnd );
4197 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4199 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4200 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4201 WIN_ReleasePtr( wndPtr );
4202 return TRUE;
4204 return FALSE;
4208 /**********************************************************************
4209 * GetMenu (USER32.@)
4211 HMENU WINAPI GetMenu( HWND hWnd )
4213 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4214 TRACE("for %p returning %p\n", hWnd, retvalue);
4215 return retvalue;
4218 /**********************************************************************
4219 * GetMenuBarInfo (USER32.@)
4221 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4223 POPUPMENU *menu;
4224 HMENU hmenu = NULL;
4225 ATOM class_atom;
4227 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4229 switch (idObject)
4231 case OBJID_CLIENT:
4232 class_atom = GetClassLongW(hwnd, GCW_ATOM);
4233 if (!class_atom)
4234 return FALSE;
4235 if (class_atom != POPUPMENU_CLASS_ATOM)
4237 WARN("called on invalid window: %d\n", class_atom);
4238 SetLastError(ERROR_INVALID_MENU_HANDLE);
4239 return FALSE;
4242 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
4243 break;
4244 case OBJID_MENU:
4245 hmenu = GetMenu(hwnd);
4246 break;
4247 case OBJID_SYSMENU:
4248 hmenu = GetSystemMenu(hwnd, FALSE);
4249 break;
4250 default:
4251 return FALSE;
4254 if (!hmenu)
4255 return FALSE;
4257 if (pmbi->cbSize != sizeof(MENUBARINFO))
4259 SetLastError(ERROR_INVALID_PARAMETER);
4260 return FALSE;
4263 menu = MENU_GetMenu(hmenu);
4264 if (!menu)
4265 return FALSE;
4266 if (idItem < 0 || idItem > menu->nItems)
4267 return FALSE;
4269 if (!menu->Height)
4271 SetRectEmpty(&pmbi->rcBar);
4273 else if (idItem == 0)
4275 GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
4276 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
4277 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
4279 else
4281 GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
4284 pmbi->hMenu = hmenu;
4285 pmbi->hwndMenu = NULL;
4286 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4287 if (idItem)
4289 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4290 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4292 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4293 if (menu)
4294 pmbi->hwndMenu = menu->hWnd;
4297 else
4299 pmbi->fFocused = pmbi->fBarFocused;
4302 return TRUE;
4305 /**********************************************************************
4306 * MENU_SetMenu
4308 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4309 * SetWindowPos call that would result if SetMenu were called directly.
4311 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4313 TRACE("(%p, %p);\n", hWnd, hMenu);
4315 if (hMenu && !IsMenu(hMenu))
4317 WARN("hMenu %p is not a menu handle\n", hMenu);
4318 return FALSE;
4320 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4321 return FALSE;
4323 hWnd = WIN_GetFullHandle( hWnd );
4324 if (GetCapture() == hWnd)
4325 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4327 if (hMenu != 0)
4329 LPPOPUPMENU lpmenu;
4331 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4333 lpmenu->hWnd = hWnd;
4334 lpmenu->Height = 0; /* Make sure we recalculate the size */
4336 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4337 return TRUE;
4341 /**********************************************************************
4342 * SetMenu (USER32.@)
4344 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4346 if(!MENU_SetMenu(hWnd, hMenu))
4347 return FALSE;
4349 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4350 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4351 return TRUE;
4355 /**********************************************************************
4356 * GetSubMenu (USER32.@)
4358 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4360 MENUITEM * lpmi;
4362 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4363 if (!(lpmi->fType & MF_POPUP)) return 0;
4364 return lpmi->hSubMenu;
4368 /**********************************************************************
4369 * DrawMenuBar (USER32.@)
4371 BOOL WINAPI DrawMenuBar( HWND hWnd )
4373 LPPOPUPMENU lppop;
4374 HMENU hMenu;
4376 if (!IsWindow( hWnd ))
4377 return FALSE;
4378 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4379 return TRUE;
4381 if ((hMenu = GetMenu( hWnd )) && (lppop = MENU_GetMenu( hMenu ))) {
4382 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4383 lppop->hwndOwner = hWnd;
4386 return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4387 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4390 /***********************************************************************
4391 * DrawMenuBarTemp (USER32.@)
4393 * UNDOCUMENTED !!
4395 * called by W98SE desk.cpl Control Panel Applet
4397 * Not 100% sure about the param names, but close.
4399 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4401 LPPOPUPMENU lppop;
4402 UINT i,retvalue;
4403 HFONT hfontOld = 0;
4404 BOOL flat_menu = FALSE;
4406 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4408 if (!hMenu)
4409 hMenu = GetMenu(hwnd);
4411 if (!hFont)
4412 hFont = get_menu_font(FALSE);
4414 lppop = MENU_GetMenu( hMenu );
4415 if (lppop == NULL || lprect == NULL)
4417 retvalue = GetSystemMetrics(SM_CYMENU);
4418 goto END;
4421 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4423 hfontOld = SelectObject( hDC, hFont);
4425 if (lppop->Height == 0)
4426 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4428 lprect->bottom = lprect->top + lppop->Height;
4430 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4432 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4433 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4434 LineTo( hDC, lprect->right, lprect->bottom );
4436 if (lppop->nItems == 0)
4438 retvalue = GetSystemMetrics(SM_CYMENU);
4439 goto END;
4442 for (i = 0; i < lppop->nItems; i++)
4444 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4445 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4447 retvalue = lppop->Height;
4449 END:
4450 if (hfontOld) SelectObject (hDC, hfontOld);
4451 return retvalue;
4454 /***********************************************************************
4455 * EndMenu (USER.187)
4456 * EndMenu (USER32.@)
4458 BOOL WINAPI EndMenu(void)
4460 /* if we are in the menu code, and it is active */
4461 if (!fEndMenu && top_popup)
4463 /* terminate the menu handling code */
4464 fEndMenu = TRUE;
4466 /* needs to be posted to wakeup the internal menu handler */
4467 /* which will now terminate the menu, in the event that */
4468 /* the main window was minimized, or lost focus, so we */
4469 /* don't end up with an orphaned menu */
4470 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4472 return fEndMenu;
4476 /*****************************************************************
4477 * LoadMenuA (USER32.@)
4479 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4481 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4482 if (!hrsrc) return 0;
4483 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4487 /*****************************************************************
4488 * LoadMenuW (USER32.@)
4490 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4492 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4493 if (!hrsrc) return 0;
4494 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4498 /**********************************************************************
4499 * LoadMenuIndirectW (USER32.@)
4501 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4503 HMENU hMenu;
4504 WORD version, offset;
4505 LPCSTR p = template;
4507 version = GET_WORD(p);
4508 p += sizeof(WORD);
4509 TRACE("%p, ver %d\n", template, version );
4510 switch (version)
4512 case 0: /* standard format is version of 0 */
4513 offset = GET_WORD(p);
4514 p += sizeof(WORD) + offset;
4515 if (!(hMenu = CreateMenu())) return 0;
4516 if (!MENU_ParseResource( p, hMenu ))
4518 DestroyMenu( hMenu );
4519 return 0;
4521 return hMenu;
4522 case 1: /* extended format is version of 1 */
4523 offset = GET_WORD(p);
4524 p += sizeof(WORD) + offset;
4525 if (!(hMenu = CreateMenu())) return 0;
4526 if (!MENUEX_ParseResource( p, hMenu))
4528 DestroyMenu( hMenu );
4529 return 0;
4531 return hMenu;
4532 default:
4533 ERR("version %d not supported.\n", version);
4534 return 0;
4539 /**********************************************************************
4540 * LoadMenuIndirectA (USER32.@)
4542 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4544 return LoadMenuIndirectW( template );
4548 /**********************************************************************
4549 * IsMenu (USER32.@)
4551 BOOL WINAPI IsMenu(HMENU hmenu)
4553 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4555 if (!menu)
4557 SetLastError(ERROR_INVALID_MENU_HANDLE);
4558 return FALSE;
4560 return TRUE;
4563 /**********************************************************************
4564 * GetMenuItemInfo_common
4567 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4568 LPMENUITEMINFOW lpmii, BOOL unicode)
4570 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4572 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4574 if (!menu) {
4575 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4576 return FALSE;
4579 if( lpmii->fMask & MIIM_TYPE) {
4580 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4581 WARN("invalid combination of fMask bits used\n");
4582 /* this does not happen on Win9x/ME */
4583 SetLastError( ERROR_INVALID_PARAMETER);
4584 return FALSE;
4586 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4587 if (menu->hbmpItem && !IS_MAGIC_BITMAP(menu->hbmpItem))
4588 lpmii->fType |= MFT_BITMAP;
4589 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4590 if( lpmii->fType & MFT_BITMAP) {
4591 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4592 lpmii->cch = 0;
4593 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4594 /* this does not happen on Win9x/ME */
4595 lpmii->dwTypeData = 0;
4596 lpmii->cch = 0;
4600 /* copy the text string */
4601 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4602 if( !menu->text ) {
4603 if(lpmii->dwTypeData && lpmii->cch) {
4604 if( unicode)
4605 *((WCHAR *)lpmii->dwTypeData) = 0;
4606 else
4607 *((CHAR *)lpmii->dwTypeData) = 0;
4609 lpmii->cch = 0;
4610 } else {
4611 int len;
4612 if (unicode)
4614 len = strlenW(menu->text);
4615 if(lpmii->dwTypeData && lpmii->cch)
4616 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4618 else
4620 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4621 0, NULL, NULL ) - 1;
4622 if(lpmii->dwTypeData && lpmii->cch)
4623 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4624 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4625 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4627 /* if we've copied a substring we return its length */
4628 if(lpmii->dwTypeData && lpmii->cch)
4629 if (lpmii->cch <= len + 1)
4630 lpmii->cch--;
4631 else
4632 lpmii->cch = len;
4633 else {
4634 /* return length of string */
4635 /* not on Win9x/ME if fType & MFT_BITMAP */
4636 lpmii->cch = len;
4641 if (lpmii->fMask & MIIM_FTYPE)
4642 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4644 if (lpmii->fMask & MIIM_BITMAP)
4645 lpmii->hbmpItem = menu->hbmpItem;
4647 if (lpmii->fMask & MIIM_STATE)
4648 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4650 if (lpmii->fMask & MIIM_ID)
4651 lpmii->wID = menu->wID;
4653 if (lpmii->fMask & MIIM_SUBMENU)
4654 lpmii->hSubMenu = menu->hSubMenu;
4655 else {
4656 /* hSubMenu is always cleared
4657 * (not on Win9x/ME ) */
4658 lpmii->hSubMenu = 0;
4661 if (lpmii->fMask & MIIM_CHECKMARKS) {
4662 lpmii->hbmpChecked = menu->hCheckBit;
4663 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4665 if (lpmii->fMask & MIIM_DATA)
4666 lpmii->dwItemData = menu->dwItemData;
4668 return TRUE;
4671 /**********************************************************************
4672 * GetMenuItemInfoA (USER32.@)
4674 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4675 LPMENUITEMINFOA lpmii)
4677 BOOL ret;
4678 MENUITEMINFOA mii;
4679 if( lpmii->cbSize != sizeof( mii) &&
4680 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4681 SetLastError( ERROR_INVALID_PARAMETER);
4682 return FALSE;
4684 memcpy( &mii, lpmii, lpmii->cbSize);
4685 mii.cbSize = sizeof( mii);
4686 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4687 (LPMENUITEMINFOW)&mii, FALSE);
4688 mii.cbSize = lpmii->cbSize;
4689 memcpy( lpmii, &mii, mii.cbSize);
4690 return ret;
4693 /**********************************************************************
4694 * GetMenuItemInfoW (USER32.@)
4696 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4697 LPMENUITEMINFOW lpmii)
4699 BOOL ret;
4700 MENUITEMINFOW mii;
4701 if( lpmii->cbSize != sizeof( mii) &&
4702 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4703 SetLastError( ERROR_INVALID_PARAMETER);
4704 return FALSE;
4706 memcpy( &mii, lpmii, lpmii->cbSize);
4707 mii.cbSize = sizeof( mii);
4708 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4709 mii.cbSize = lpmii->cbSize;
4710 memcpy( lpmii, &mii, mii.cbSize);
4711 return ret;
4715 /* set a menu item text from a ASCII or Unicode string */
4716 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4718 if (!text)
4719 menu->text = NULL;
4720 else if (unicode)
4722 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4723 strcpyW( menu->text, text );
4725 else
4727 LPCSTR str = (LPCSTR)text;
4728 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4729 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4730 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4735 /**********************************************************************
4736 * MENU_depth
4738 * detect if there are loops in the menu tree (or the depth is too large)
4740 static int MENU_depth( POPUPMENU *pmenu, int depth)
4742 UINT i;
4743 MENUITEM *item;
4744 int subdepth;
4746 depth++;
4747 if( depth > MAXMENUDEPTH) return depth;
4748 item = pmenu->items;
4749 subdepth = depth;
4750 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4751 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4752 if( psubmenu){
4753 int bdepth = MENU_depth( psubmenu, depth);
4754 if( bdepth > subdepth) subdepth = bdepth;
4756 if( subdepth > MAXMENUDEPTH)
4757 TRACE("<- hmenu %p\n", item->hSubMenu);
4759 return subdepth;
4763 /**********************************************************************
4764 * SetMenuItemInfo_common
4766 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4767 * MIIM_BITMAP and MIIM_STRING flags instead.
4770 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4771 const MENUITEMINFOW *lpmii,
4772 BOOL unicode)
4774 if (!menu) return FALSE;
4776 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4778 if (lpmii->fMask & MIIM_FTYPE ) {
4779 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4780 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4782 if (lpmii->fMask & MIIM_STRING ) {
4783 /* free the string when used */
4784 HeapFree(GetProcessHeap(), 0, menu->text);
4785 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4788 if (lpmii->fMask & MIIM_STATE)
4789 /* Other menu items having MFS_DEFAULT are not converted
4790 to normal items */
4791 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4793 if (lpmii->fMask & MIIM_ID)
4794 menu->wID = lpmii->wID;
4796 if (lpmii->fMask & MIIM_SUBMENU) {
4797 menu->hSubMenu = lpmii->hSubMenu;
4798 if (menu->hSubMenu) {
4799 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4800 if (subMenu) {
4801 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4802 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4803 menu->hSubMenu = 0;
4804 return FALSE;
4806 subMenu->wFlags |= MF_POPUP;
4807 menu->fType |= MF_POPUP;
4808 } else {
4809 SetLastError( ERROR_INVALID_PARAMETER);
4810 return FALSE;
4813 else
4814 menu->fType &= ~MF_POPUP;
4817 if (lpmii->fMask & MIIM_CHECKMARKS)
4819 menu->hCheckBit = lpmii->hbmpChecked;
4820 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4822 if (lpmii->fMask & MIIM_DATA)
4823 menu->dwItemData = lpmii->dwItemData;
4825 if (lpmii->fMask & MIIM_BITMAP)
4826 menu->hbmpItem = lpmii->hbmpItem;
4828 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4829 menu->fType |= MFT_SEPARATOR;
4831 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4832 return TRUE;
4835 /**********************************************************************
4836 * MENU_NormalizeMenuItemInfoStruct
4838 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4839 * check, copy and extend the MENUITEMINFO struct from the version that the application
4840 * supplied to the version used by wine source. */
4841 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4842 MENUITEMINFOW *pmii_out )
4844 /* do we recognize the size? */
4845 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4846 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4847 SetLastError( ERROR_INVALID_PARAMETER);
4848 return FALSE;
4850 /* copy the fields that we have */
4851 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4852 /* if the hbmpItem member is missing then extend */
4853 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4854 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4855 pmii_out->hbmpItem = NULL;
4857 /* test for invalid bit combinations */
4858 if( (pmii_out->fMask & MIIM_TYPE &&
4859 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4860 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4861 WARN("invalid combination of fMask bits used\n");
4862 /* this does not happen on Win9x/ME */
4863 SetLastError( ERROR_INVALID_PARAMETER);
4864 return FALSE;
4866 /* convert old style (MIIM_TYPE) to the new */
4867 if( pmii_out->fMask & MIIM_TYPE){
4868 pmii_out->fMask |= MIIM_FTYPE;
4869 if( IS_STRING_ITEM(pmii_out->fType)){
4870 pmii_out->fMask |= MIIM_STRING;
4871 } else if( (pmii_out->fType) & MFT_BITMAP){
4872 pmii_out->fMask |= MIIM_BITMAP;
4873 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4876 return TRUE;
4879 /**********************************************************************
4880 * SetMenuItemInfoA (USER32.@)
4882 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4883 const MENUITEMINFOA *lpmii)
4885 MENUITEM *menuitem;
4886 MENUITEMINFOW mii;
4888 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4890 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4892 if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
4894 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4895 if (item == SC_TASKLIST && !bypos) return TRUE;
4896 return FALSE;
4898 return SetMenuItemInfo_common( menuitem, &mii, FALSE );
4901 /**********************************************************************
4902 * SetMenuItemInfoW (USER32.@)
4904 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4905 const MENUITEMINFOW *lpmii)
4907 MENUITEM *menuitem;
4908 MENUITEMINFOW mii;
4910 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4912 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4913 if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
4915 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4916 if (item == SC_TASKLIST && !bypos) return TRUE;
4917 return FALSE;
4919 return SetMenuItemInfo_common( menuitem, &mii, TRUE );
4922 /**********************************************************************
4923 * SetMenuDefaultItem (USER32.@)
4926 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4928 UINT i;
4929 POPUPMENU *menu;
4930 MENUITEM *item;
4932 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4934 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4936 /* reset all default-item flags */
4937 item = menu->items;
4938 for (i = 0; i < menu->nItems; i++, item++)
4940 item->fState &= ~MFS_DEFAULT;
4943 /* no default item */
4944 if ( -1 == uItem)
4946 return TRUE;
4949 item = menu->items;
4950 if ( bypos )
4952 if ( uItem >= menu->nItems ) return FALSE;
4953 item[uItem].fState |= MFS_DEFAULT;
4954 return TRUE;
4956 else
4958 for (i = 0; i < menu->nItems; i++, item++)
4960 if (item->wID == uItem)
4962 item->fState |= MFS_DEFAULT;
4963 return TRUE;
4968 return FALSE;
4971 /**********************************************************************
4972 * GetMenuDefaultItem (USER32.@)
4974 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4976 POPUPMENU *menu;
4977 MENUITEM * item;
4978 UINT i = 0;
4980 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4982 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4984 /* find default item */
4985 item = menu->items;
4987 /* empty menu */
4988 if (! item) return -1;
4990 while ( !( item->fState & MFS_DEFAULT ) )
4992 i++; item++;
4993 if (i >= menu->nItems ) return -1;
4996 /* default: don't return disabled items */
4997 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4999 /* search rekursiv when needed */
5000 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
5002 UINT ret;
5003 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
5004 if ( -1 != ret ) return ret;
5006 /* when item not found in submenu, return the popup item */
5008 return ( bypos ) ? i : item->wID;
5013 /**********************************************************************
5014 * InsertMenuItemA (USER32.@)
5016 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
5017 const MENUITEMINFOA *lpmii)
5019 MENUITEM *item;
5020 MENUITEMINFOW mii;
5022 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5024 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5026 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5027 return SetMenuItemInfo_common(item, &mii, FALSE);
5031 /**********************************************************************
5032 * InsertMenuItemW (USER32.@)
5034 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
5035 const MENUITEMINFOW *lpmii)
5037 MENUITEM *item;
5038 MENUITEMINFOW mii;
5040 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
5042 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5044 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5045 return SetMenuItemInfo_common(item, &mii, TRUE);
5048 /**********************************************************************
5049 * CheckMenuRadioItem (USER32.@)
5052 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
5053 UINT first, UINT last, UINT check,
5054 UINT bypos)
5056 BOOL done = FALSE;
5057 UINT i;
5058 MENUITEM *mi_first = NULL, *mi_check;
5059 HMENU m_first, m_check;
5061 for (i = first; i <= last; i++)
5063 UINT pos = i;
5065 if (!mi_first)
5067 m_first = hMenu;
5068 mi_first = MENU_FindItem(&m_first, &pos, bypos);
5069 if (!mi_first) continue;
5070 mi_check = mi_first;
5071 m_check = m_first;
5073 else
5075 m_check = hMenu;
5076 mi_check = MENU_FindItem(&m_check, &pos, bypos);
5077 if (!mi_check) continue;
5080 if (m_first != m_check) continue;
5081 if (mi_check->fType == MFT_SEPARATOR) continue;
5083 if (i == check)
5085 mi_check->fType |= MFT_RADIOCHECK;
5086 mi_check->fState |= MFS_CHECKED;
5087 done = TRUE;
5089 else
5091 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5092 mi_check->fState &= ~MFS_CHECKED;
5096 return done;
5100 /**********************************************************************
5101 * GetMenuItemRect (USER32.@)
5103 * ATTENTION: Here, the returned values in rect are the screen
5104 * coordinates of the item just like if the menu was
5105 * always on the upper left side of the application.
5108 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5109 LPRECT rect)
5111 POPUPMENU *itemMenu;
5112 MENUITEM *item;
5113 HWND referenceHwnd;
5115 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5117 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5118 referenceHwnd = hwnd;
5120 if(!hwnd)
5122 itemMenu = MENU_GetMenu(hMenu);
5123 if (itemMenu == NULL)
5124 return FALSE;
5126 if(itemMenu->hWnd == 0)
5127 return FALSE;
5128 referenceHwnd = itemMenu->hWnd;
5131 if ((rect == NULL) || (item == NULL))
5132 return FALSE;
5134 *rect = item->rect;
5136 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5138 return TRUE;
5141 /**********************************************************************
5142 * SetMenuInfo (USER32.@)
5144 * FIXME
5145 * actually use the items to draw the menu
5146 * (recalculate and/or redraw)
5148 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5150 POPUPMENU *menu;
5151 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5153 if (lpmi->fMask & MIM_BACKGROUND)
5154 menu->hbrBack = lpmi->hbrBack;
5156 if (lpmi->fMask & MIM_HELPID)
5157 menu->dwContextHelpID = lpmi->dwContextHelpID;
5159 if (lpmi->fMask & MIM_MAXHEIGHT)
5160 menu->cyMax = lpmi->cyMax;
5162 if (lpmi->fMask & MIM_MENUDATA)
5163 menu->dwMenuData = lpmi->dwMenuData;
5165 if (lpmi->fMask & MIM_STYLE)
5166 menu->dwStyle = lpmi->dwStyle;
5168 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5169 int i;
5170 MENUITEM *item = menu->items;
5171 for( i = menu->nItems; i; i--, item++)
5172 if( item->fType & MF_POPUP)
5173 menu_SetMenuInfo( item->hSubMenu, lpmi);
5175 return TRUE;
5178 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5180 TRACE("(%p %p)\n", hMenu, lpmi);
5181 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5182 if( lpmi->fMask & MIM_STYLE) {
5183 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5184 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5185 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5187 return TRUE;
5189 SetLastError( ERROR_INVALID_PARAMETER);
5190 return FALSE;
5193 /**********************************************************************
5194 * GetMenuInfo (USER32.@)
5196 * NOTES
5197 * win98/NT5.0
5200 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5201 { POPUPMENU *menu;
5203 TRACE("(%p %p)\n", hMenu, lpmi);
5205 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5208 if (lpmi->fMask & MIM_BACKGROUND)
5209 lpmi->hbrBack = menu->hbrBack;
5211 if (lpmi->fMask & MIM_HELPID)
5212 lpmi->dwContextHelpID = menu->dwContextHelpID;
5214 if (lpmi->fMask & MIM_MAXHEIGHT)
5215 lpmi->cyMax = menu->cyMax;
5217 if (lpmi->fMask & MIM_MENUDATA)
5218 lpmi->dwMenuData = menu->dwMenuData;
5220 if (lpmi->fMask & MIM_STYLE)
5221 lpmi->dwStyle = menu->dwStyle;
5223 return TRUE;
5225 SetLastError( ERROR_INVALID_PARAMETER);
5226 return FALSE;
5230 /**********************************************************************
5231 * SetMenuContextHelpId (USER32.@)
5233 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5235 LPPOPUPMENU menu;
5237 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5239 if ((menu = MENU_GetMenu(hMenu)))
5241 menu->dwContextHelpID = dwContextHelpID;
5242 return TRUE;
5244 return FALSE;
5248 /**********************************************************************
5249 * GetMenuContextHelpId (USER32.@)
5251 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5253 LPPOPUPMENU menu;
5255 TRACE("(%p)\n", hMenu);
5257 if ((menu = MENU_GetMenu(hMenu)))
5259 return menu->dwContextHelpID;
5261 return 0;
5264 /**********************************************************************
5265 * MenuItemFromPoint (USER32.@)
5267 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5269 POPUPMENU *menu = MENU_GetMenu(hMenu);
5270 UINT pos;
5272 /*FIXME: Do we have to handle hWnd here? */
5273 if (!menu) return -1;
5274 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5275 return pos;
5279 /**********************************************************************
5280 * translate_accelerator
5282 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5283 BYTE fVirt, WORD key, WORD cmd )
5285 INT mask = 0;
5286 UINT mesg = 0;
5288 if (wParam != key) return FALSE;
5290 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5291 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5292 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5294 if (message == WM_CHAR || message == WM_SYSCHAR)
5296 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5298 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5299 goto found;
5302 else
5304 if(fVirt & FVIRTKEY)
5306 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5307 wParam, 0xff & HIWORD(lParam));
5309 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5310 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5312 else
5314 if (!(lParam & 0x01000000)) /* no special_key */
5316 if ((fVirt & FALT) && (lParam & 0x20000000))
5317 { /* ^^ ALT pressed */
5318 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5319 goto found;
5324 return FALSE;
5326 found:
5327 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5328 mesg = 1;
5329 else
5331 HMENU hMenu, hSubMenu, hSysMenu;
5332 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5334 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5335 hSysMenu = get_win_sys_menu( hWnd );
5337 /* find menu item and ask application to initialize it */
5338 /* 1. in the system menu */
5339 hSubMenu = hSysMenu;
5340 nPos = cmd;
5341 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5343 if (GetCapture())
5344 mesg = 2;
5345 if (!IsWindowEnabled(hWnd))
5346 mesg = 3;
5347 else
5349 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5350 if(hSubMenu != hSysMenu)
5352 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5353 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5354 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5356 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5359 else /* 2. in the window's menu */
5361 hSubMenu = hMenu;
5362 nPos = cmd;
5363 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5365 if (GetCapture())
5366 mesg = 2;
5367 if (!IsWindowEnabled(hWnd))
5368 mesg = 3;
5369 else
5371 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5372 if(hSubMenu != hMenu)
5374 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5375 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5376 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5378 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5383 if (mesg == 0)
5385 if (uSysStat != (UINT)-1)
5387 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5388 mesg=4;
5389 else
5390 mesg=WM_SYSCOMMAND;
5392 else
5394 if (uStat != (UINT)-1)
5396 if (IsIconic(hWnd))
5397 mesg=5;
5398 else
5400 if (uStat & (MF_DISABLED|MF_GRAYED))
5401 mesg=6;
5402 else
5403 mesg=WM_COMMAND;
5406 else
5407 mesg=WM_COMMAND;
5412 if( mesg==WM_COMMAND )
5414 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5415 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5417 else if( mesg==WM_SYSCOMMAND )
5419 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5420 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5422 else
5424 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5425 * #0: unknown (please report!)
5426 * #1: for WM_KEYUP,WM_SYSKEYUP
5427 * #2: mouse is captured
5428 * #3: window is disabled
5429 * #4: it's a disabled system menu option
5430 * #5: it's a menu option, but window is iconic
5431 * #6: it's a menu option, but disabled
5433 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5434 if(mesg==0)
5435 ERR_(accel)(" unknown reason - please report!\n");
5437 return TRUE;
5440 /**********************************************************************
5441 * TranslateAcceleratorA (USER32.@)
5442 * TranslateAccelerator (USER32.@)
5444 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5446 switch (msg->message)
5448 case WM_KEYDOWN:
5449 case WM_SYSKEYDOWN:
5450 return TranslateAcceleratorW( hWnd, hAccel, msg );
5452 case WM_CHAR:
5453 case WM_SYSCHAR:
5455 MSG msgW = *msg;
5456 char ch = LOWORD(msg->wParam);
5457 WCHAR wch;
5458 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5459 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5460 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5463 default:
5464 return 0;
5468 /**********************************************************************
5469 * TranslateAcceleratorW (USER32.@)
5471 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5473 ACCEL data[32], *ptr = data;
5474 int i, count;
5476 if (!hWnd) return 0;
5478 if (msg->message != WM_KEYDOWN &&
5479 msg->message != WM_SYSKEYDOWN &&
5480 msg->message != WM_CHAR &&
5481 msg->message != WM_SYSCHAR)
5482 return 0;
5484 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5485 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5487 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5488 if (count > sizeof(data)/sizeof(data[0]))
5490 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5492 count = CopyAcceleratorTableW( hAccel, ptr, count );
5493 for (i = 0; i < count; i++)
5495 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5496 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5497 break;
5499 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5500 return (i < count);