dbghelp: Use RtlGetVersion() for system version detection instead.
[wine.git] / dlls / user32 / menu.c
blobafe3c50154e974e84873cf33f52eb0bf6f349d23
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 <stdarg.h>
43 #include <string.h>
45 #define OEMRESOURCE
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winnls.h"
51 #include "wine/server.h"
52 #include "wine/exception.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
60 /* internal flags for menu tracking */
62 #define TF_ENDMENU 0x10000
63 #define TF_SUSPENDPOPUP 0x20000
64 #define TF_SKIPREMOVE 0x40000
65 #define TF_RCVD_BTN_UP 0x80000
67 typedef struct
69 UINT trackFlags;
70 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
71 HMENU hTopMenu; /* initial menu */
72 HWND hOwnerWnd; /* where notifications are sent */
73 POINT pt;
74 } MTRACKER;
76 #define ITEM_PREV -1
77 #define ITEM_NEXT 1
79 /* Internal MENU_TrackMenu() flags */
80 #define TPM_INTERNAL 0xF0000000
81 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
82 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
84 /* Space between 2 columns */
85 #define MENU_COL_SPACE 4
87 /* Margins for popup menus */
88 #define MENU_MARGIN 3
90 /* (other menu->FocusedItem values give the position of the focused item) */
91 #define NO_SELECTED_ITEM 0xffff
93 #define MENU_ITEM_TYPE(flags) \
94 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
96 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
97 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
98 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
100 #define IS_SYSTEM_MENU(menu) \
101 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
103 #define MENUITEMINFO_TYPE_MASK \
104 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
105 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
106 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
107 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
108 #define STATE_MASK (~TYPE_MASK)
109 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
111 static SIZE menucharsize;
112 static UINT ODitemheight; /* default owner drawn item height */
114 /* Use global popup window because there's no way 2 menus can
115 * be tracked at the same time. */
116 static HWND top_popup;
117 static HMENU top_popup_hmenu;
119 /* Flag set by EndMenu() to force an exit from menu tracking */
120 static BOOL fEndMenu = FALSE;
122 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
124 static BOOL is_win_menu_disallowed(HWND hwnd)
126 return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
129 /*********************************************************************
130 * menu class descriptor
132 const struct builtin_class_descr MENU_builtin_class =
134 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
135 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
136 WINPROC_MENU, /* proc */
137 sizeof(HMENU), /* extra */
138 IDC_ARROW, /* cursor */
139 (HBRUSH)(COLOR_MENU+1) /* brush */
143 /***********************************************************************
144 * debug_print_menuitem
146 * Print a menuitem in readable form.
149 #define debug_print_menuitem(pre, mp, post) \
150 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
152 #define MENUOUT(text) \
153 TRACE("%s%s", (count++ ? "," : ""), (text))
155 #define MENUFLAG(bit,text) \
156 do { \
157 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
158 } while (0)
160 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
161 const char *postfix)
163 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
164 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
165 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
166 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
167 TRACE("%s ", prefix);
168 if (mp) {
169 UINT flags = mp->fType;
170 TRACE( "{ ID=0x%lx", mp->wID);
171 if ( mp->hSubMenu)
172 TRACE( ", Sub=%p", mp->hSubMenu);
173 if (flags) {
174 int count = 0;
175 TRACE( ", fType=");
176 MENUFLAG( MFT_SEPARATOR, "sep");
177 MENUFLAG( MFT_OWNERDRAW, "own");
178 MENUFLAG( MFT_BITMAP, "bit");
179 MENUFLAG(MF_POPUP, "pop");
180 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
181 MENUFLAG(MFT_MENUBREAK, "brk");
182 MENUFLAG(MFT_RADIOCHECK, "radio");
183 MENUFLAG(MFT_RIGHTORDER, "rorder");
184 MENUFLAG(MF_SYSMENU, "sys");
185 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
186 if (flags)
187 TRACE( "+0x%x", flags);
189 flags = mp->fState;
190 if (flags) {
191 int count = 0;
192 TRACE( ", State=");
193 MENUFLAG(MFS_GRAYED, "grey");
194 MENUFLAG(MFS_DEFAULT, "default");
195 MENUFLAG(MFS_DISABLED, "dis");
196 MENUFLAG(MFS_CHECKED, "check");
197 MENUFLAG(MFS_HILITE, "hi");
198 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
199 MENUFLAG(MF_MOUSESELECT, "mouse");
200 if (flags)
201 TRACE( "+0x%x", flags);
203 if (mp->hCheckBit)
204 TRACE( ", Chk=%p", mp->hCheckBit);
205 if (mp->hUnCheckBit)
206 TRACE( ", Unc=%p", mp->hUnCheckBit);
207 if (mp->text)
208 TRACE( ", Text=%s", debugstr_w(mp->text));
209 if (mp->dwItemData)
210 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
211 if (mp->hbmpItem)
213 if( IS_MAGIC_BITMAP(mp->hbmpItem))
214 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
215 else
216 TRACE( ", hbitmap=%p", mp->hbmpItem);
218 TRACE( " }");
219 } else
220 TRACE( "NULL");
221 TRACE(" %s\n", postfix);
224 #undef MENUOUT
225 #undef MENUFLAG
228 /***********************************************************************
229 * MENU_GetMenu
231 * Validate the given menu handle and returns the menu structure pointer.
233 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
235 POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU );
237 if (menu == OBJ_OTHER_PROCESS)
239 WARN( "other process menu %p?\n", hMenu);
240 return NULL;
242 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
243 else WARN("invalid menu handle=%p\n", hMenu);
244 return menu;
247 static POPUPMENU *grab_menu_ptr(HMENU hMenu)
249 POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU );
251 if (menu == OBJ_OTHER_PROCESS)
253 WARN("other process menu %p?\n", hMenu);
254 return NULL;
257 if (menu)
258 menu->refcount++;
259 else
260 WARN("invalid menu handle=%p\n", hMenu);
261 return menu;
264 static void release_menu_ptr(POPUPMENU *menu)
266 if (menu)
268 menu->refcount--;
269 release_user_handle_ptr(menu);
273 /***********************************************************************
274 * get_win_sys_menu
276 * Get the system menu of a window
278 static HMENU get_win_sys_menu( HWND hwnd )
280 HMENU ret = 0;
281 WND *win = WIN_GetPtr( hwnd );
282 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
284 ret = win->hSysMenu;
285 WIN_ReleasePtr( win );
287 return ret;
290 /***********************************************************************
291 * get_menu_font
293 static HFONT get_menu_font( BOOL bold )
295 static HFONT hMenuFont, hMenuFontBold;
297 HFONT ret = bold ? hMenuFontBold : hMenuFont;
299 if (!ret)
301 NONCLIENTMETRICSW ncm;
302 HFONT prev;
304 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
305 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
307 if (bold)
309 ncm.lfMenuFont.lfWeight += 300;
310 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
312 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
313 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
314 ret, NULL );
315 if (prev)
317 /* another thread beat us to it */
318 DeleteObject( ret );
319 ret = prev;
322 return ret;
325 /***********************************************************************
326 * get_arrow_bitmap
328 static HBITMAP get_arrow_bitmap(void)
330 static HBITMAP arrow_bitmap;
332 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
333 return arrow_bitmap;
336 static inline UINT get_scroll_arrow_height(const POPUPMENU *menu)
338 return menucharsize.cy + 4;
341 /***********************************************************************
342 * MENU_CopySysPopup
344 * Return the default system menu.
346 static HMENU MENU_CopySysPopup(BOOL mdi)
348 HMENU hMenu = LoadMenuW(user32_module, mdi ? L"SYSMENUMDI" : L"SYSMENU");
350 if( hMenu ) {
351 MENUINFO minfo;
352 MENUITEMINFOW miteminfo;
353 POPUPMENU* menu = MENU_GetMenu(hMenu);
354 menu->wFlags |= MF_SYSMENU | MF_POPUP;
355 /* decorate the menu with bitmaps */
356 minfo.cbSize = sizeof( MENUINFO);
357 minfo.dwStyle = MNS_CHECKORBMP;
358 minfo.fMask = MIM_STYLE;
359 SetMenuInfo( hMenu, &minfo);
360 miteminfo.cbSize = sizeof( MENUITEMINFOW);
361 miteminfo.fMask = MIIM_BITMAP;
362 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
363 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
364 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
365 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
366 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
367 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
368 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
369 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
370 NtUserSetMenuDefaultItem( hMenu, SC_CLOSE, FALSE );
372 else
373 ERR("Unable to load default system menu\n" );
375 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
377 return hMenu;
381 /**********************************************************************
382 * MENU_GetSysMenu
384 * Create a copy of the system menu. System menu in Windows is
385 * a special menu bar with the single entry - system menu popup.
386 * This popup is presented to the outside world as a "system menu".
387 * However, the real system menu handle is sometimes seen in the
388 * WM_MENUSELECT parameters (and Word 6 likes it this way).
390 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
392 HMENU hMenu;
394 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
395 if ((hMenu = CreateMenu()))
397 POPUPMENU *menu = MENU_GetMenu(hMenu);
398 menu->wFlags = MF_SYSMENU;
399 menu->hWnd = WIN_GetFullHandle( hWnd );
400 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
402 if (!hPopupMenu)
404 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
405 hPopupMenu = MENU_CopySysPopup(TRUE);
406 else
407 hPopupMenu = MENU_CopySysPopup(FALSE);
410 if (hPopupMenu)
412 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
413 NtUserDeleteMenu( hPopupMenu, SC_CLOSE, MF_BYCOMMAND );
415 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
416 (UINT_PTR)hPopupMenu, NULL );
418 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
419 menu->items[0].fState = 0;
420 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
422 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
423 return hMenu;
425 NtUserDestroyMenu( hMenu );
427 ERR("failed to load system menu!\n");
428 return 0;
432 /***********************************************************************
433 * MENU_InitSysMenuPopup
435 * Grey the appropriate items in System menu.
437 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
439 BOOL gray;
441 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
442 NtUserEnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
443 gray = ((style & WS_MAXIMIZE) != 0);
444 NtUserEnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
445 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
446 NtUserEnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
447 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
448 NtUserEnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
449 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
450 NtUserEnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
451 gray = (clsStyle & CS_NOCLOSE) != 0;
453 /* The menu item must keep its state if it's disabled */
454 if(gray)
455 NtUserEnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
459 /******************************************************************************
461 * UINT MENU_GetStartOfNextColumn(
462 * HMENU hMenu )
464 *****************************************************************************/
466 static UINT MENU_GetStartOfNextColumn(
467 HMENU hMenu )
469 POPUPMENU *menu = MENU_GetMenu(hMenu);
470 UINT i;
472 if(!menu)
473 return NO_SELECTED_ITEM;
475 i = menu->FocusedItem + 1;
476 if( i == NO_SELECTED_ITEM )
477 return i;
479 for( ; i < menu->nItems; ++i ) {
480 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
481 return i;
484 return NO_SELECTED_ITEM;
488 /******************************************************************************
490 * UINT MENU_GetStartOfPrevColumn(
491 * HMENU hMenu )
493 *****************************************************************************/
495 static UINT MENU_GetStartOfPrevColumn(
496 HMENU hMenu )
498 POPUPMENU *menu = MENU_GetMenu(hMenu);
499 UINT i;
501 if( !menu )
502 return NO_SELECTED_ITEM;
504 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
505 return NO_SELECTED_ITEM;
507 /* Find the start of the column */
509 for(i = menu->FocusedItem; i != 0 &&
510 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
511 --i); /* empty */
513 if(i == 0)
514 return NO_SELECTED_ITEM;
516 for(--i; i != 0; --i) {
517 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
518 break;
521 TRACE("ret %d.\n", i );
523 return i;
526 static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
528 UINT fallback_pos = ~0u, i;
529 POPUPMENU *menu;
531 menu = grab_menu_ptr(hmenu);
532 if (!menu)
533 return NULL;
535 if (flags & MF_BYPOSITION)
537 if (id >= menu->nItems)
539 release_menu_ptr(menu);
540 return NULL;
543 if (pos) *pos = id;
544 return menu;
546 else
548 MENUITEM *item = menu->items;
549 for (i = 0; i < menu->nItems; i++, item++)
551 if (item->fType & MF_POPUP)
553 POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
555 if (submenu)
557 release_menu_ptr(menu);
558 return submenu;
560 else if (item->wID == id)
562 /* fallback to this item if nothing else found */
563 fallback_pos = i;
566 else if (item->wID == id)
568 if (pos) *pos = i;
569 return menu;
574 if (fallback_pos != ~0u)
575 *pos = fallback_pos;
576 else
578 release_menu_ptr(menu);
579 menu = NULL;
582 return menu;
585 /***********************************************************************
586 * MENU_FindSubMenu
588 * Find a Sub menu. Return the position of the submenu, and modifies
589 * *hmenu in case it is found in another sub-menu.
590 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
592 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
594 POPUPMENU *menu;
595 UINT i;
596 MENUITEM *item;
597 if (((*hmenu)==(HMENU)0xffff) ||
598 (!(menu = MENU_GetMenu(*hmenu))))
599 return NO_SELECTED_ITEM;
600 item = menu->items;
601 for (i = 0; i < menu->nItems; i++, item++) {
602 if(!(item->fType & MF_POPUP)) continue;
603 if (item->hSubMenu == hSubTarget) {
604 return i;
606 else {
607 HMENU hsubmenu = item->hSubMenu;
608 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
609 if (pos != NO_SELECTED_ITEM) {
610 *hmenu = hsubmenu;
611 return pos;
615 return NO_SELECTED_ITEM;
618 /***********************************************************************
619 * MENU_AdjustMenuItemRect
621 * Adjust menu item rectangle according to scrolling state.
623 static void
624 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
626 INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
628 OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
631 enum hittest
633 ht_nowhere, /* outside the menu */
634 ht_border, /* anywhere that's not an item or a scroll arrow */
635 ht_item, /* a menu item */
636 ht_scroll_up, /* scroll up arrow */
637 ht_scroll_down /* scroll down arrow */
640 /***********************************************************************
641 * MENU_FindItemByCoords
643 * Find the item at the specified coordinates (screen coords). Does
644 * not work for child windows and therefore should not be called for
645 * an arbitrary system menu.
647 * Returns a hittest code. *pos will contain the position of the
648 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
649 * or ht_scroll_down then *pos will contain the position of the
650 * item that's just outside the items_rect - ie, the one that would
651 * be scrolled completely into view.
653 static enum hittest MENU_FindItemByCoords( const POPUPMENU *menu, POINT pt, UINT *pos )
655 MENUITEM *item;
656 UINT i;
657 RECT rect;
658 enum hittest ht = ht_border;
660 *pos = NO_SELECTED_ITEM;
662 if (!GetWindowRect(menu->hWnd, &rect) || !PtInRect(&rect, pt))
663 return ht_nowhere;
665 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
666 else pt.x -= rect.left;
667 pt.y -= rect.top;
669 if (!PtInRect(&menu->items_rect, pt))
671 if (!menu->bScrolling || pt.x < menu->items_rect.left || pt.x >= menu->items_rect.right)
672 return ht_border;
674 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
675 if (pt.y < menu->items_rect.top)
677 ht = ht_scroll_up;
678 pt.y = menu->items_rect.top - 1;
680 else
682 ht = ht_scroll_down;
683 pt.y = menu->items_rect.bottom;
687 item = menu->items;
688 for (i = 0; i < menu->nItems; i++, item++)
690 rect = item->rect;
691 MENU_AdjustMenuItemRect(menu, &rect);
692 if (PtInRect(&rect, pt))
694 *pos = i;
695 if (ht != ht_scroll_up && ht != ht_scroll_down) ht = ht_item;
696 break;
700 return ht;
704 /***********************************************************************
705 * MENU_FindItemByKey
707 * Find the menu item selected by a key press.
708 * Return item id, -1 if none, -2 if we should close the menu.
710 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
711 WCHAR key, BOOL forceMenuChar )
713 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
715 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
717 if (hmenu)
719 POPUPMENU *menu = MENU_GetMenu( hmenu );
720 MENUITEM *item = menu->items;
721 LRESULT menuchar;
723 if( !forceMenuChar )
725 UINT i;
726 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
728 for (i = 0; i < menu->nItems; i++, item++)
730 if( item->text)
732 const WCHAR *p = item->text - 2;
735 const WCHAR *q = p + 2;
736 p = wcschr (q, '&');
737 if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
739 while (p != NULL && p [1] == '&');
740 if (p && (towupper(p[1]) == towupper(key))) return i;
744 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
745 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
746 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
747 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
749 return (UINT)(-1);
753 /***********************************************************************
754 * MENU_GetBitmapItemSize
756 * Get the size of a bitmap item.
758 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
759 HWND hwndOwner)
761 BITMAP bm;
762 HBITMAP bmp = lpitem->hbmpItem;
764 size->cx = size->cy = 0;
766 /* check if there is a magic menu item associated with this item */
767 switch( (INT_PTR) bmp )
769 case (INT_PTR)HBMMENU_CALLBACK:
771 MEASUREITEMSTRUCT measItem;
772 measItem.CtlType = ODT_MENU;
773 measItem.CtlID = 0;
774 measItem.itemID = lpitem->wID;
775 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
776 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
777 measItem.itemData = lpitem->dwItemData;
778 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
779 size->cx = measItem.itemWidth;
780 size->cy = measItem.itemHeight;
781 return;
783 break;
784 case (INT_PTR)HBMMENU_SYSTEM:
785 if (lpitem->dwItemData)
787 bmp = (HBITMAP)lpitem->dwItemData;
788 break;
790 /* fall through */
791 case (INT_PTR)HBMMENU_MBAR_RESTORE:
792 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
793 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
794 case (INT_PTR)HBMMENU_MBAR_CLOSE:
795 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
796 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
797 size->cy = size->cx;
798 return;
799 case (INT_PTR)HBMMENU_POPUP_CLOSE:
800 case (INT_PTR)HBMMENU_POPUP_RESTORE:
801 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
802 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
803 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
804 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
805 return;
807 if (GetObjectW(bmp, sizeof(bm), &bm ))
809 size->cx = bm.bmWidth;
810 size->cy = bm.bmHeight;
814 /***********************************************************************
815 * MENU_DrawBitmapItem
817 * Draw a bitmap item.
819 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
820 POPUPMENU *menu, HWND hwndOwner, UINT odaction )
822 BITMAP bm;
823 DWORD rop;
824 HDC hdcMem;
825 HBITMAP bmp;
826 int w = rect->right - rect->left;
827 int h = rect->bottom - rect->top;
828 int bmp_xoffset = 0;
829 int left, top;
830 HBITMAP hbmToDraw = lpitem->hbmpItem;
831 bmp = hbmToDraw;
833 /* Check if there is a magic menu item associated with this item */
834 if (IS_MAGIC_BITMAP(hbmToDraw))
836 UINT flags = 0;
837 WCHAR bmchr = 0;
838 RECT r;
840 switch((INT_PTR)hbmToDraw)
842 case (INT_PTR)HBMMENU_SYSTEM:
843 if (lpitem->dwItemData)
845 bmp = (HBITMAP)lpitem->dwItemData;
846 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
848 else
850 static HBITMAP hBmpSysMenu;
852 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
853 bmp = hBmpSysMenu;
854 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
855 /* only use right half of the bitmap */
856 bmp_xoffset = bm.bmWidth / 2;
857 bm.bmWidth -= bmp_xoffset;
859 goto got_bitmap;
860 case (INT_PTR)HBMMENU_MBAR_RESTORE:
861 flags = DFCS_CAPTIONRESTORE;
862 break;
863 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
864 flags = DFCS_CAPTIONMIN;
865 break;
866 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
867 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
868 break;
869 case (INT_PTR)HBMMENU_MBAR_CLOSE:
870 flags = DFCS_CAPTIONCLOSE;
871 break;
872 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
873 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
874 break;
875 case (INT_PTR)HBMMENU_CALLBACK:
877 DRAWITEMSTRUCT drawItem;
878 drawItem.CtlType = ODT_MENU;
879 drawItem.CtlID = 0;
880 drawItem.itemID = lpitem->wID;
881 drawItem.itemAction = odaction;
882 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
883 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
884 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
885 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
886 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
887 drawItem.hwndItem = (HWND)menu->obj.handle;
888 drawItem.hDC = hdc;
889 drawItem.itemData = lpitem->dwItemData;
890 drawItem.rcItem = *rect;
891 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
892 return;
894 break;
895 case (INT_PTR)HBMMENU_POPUP_CLOSE:
896 bmchr = 0x72;
897 break;
898 case (INT_PTR)HBMMENU_POPUP_RESTORE:
899 bmchr = 0x32;
900 break;
901 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
902 bmchr = 0x31;
903 break;
904 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
905 bmchr = 0x30;
906 break;
907 default:
908 FIXME("Magic %p not implemented\n", hbmToDraw);
909 return;
911 if (bmchr)
913 /* draw the magic bitmaps using marlett font characters */
914 /* FIXME: fontsize and the position (x,y) could probably be better */
915 HFONT hfont, hfontsav;
916 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, L"Marlett" };
917 logfont.lfHeight = min( h, w) - 5 ;
918 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
919 hfont = CreateFontIndirectW( &logfont);
920 hfontsav = SelectObject(hdc, hfont);
921 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
922 SelectObject(hdc, hfontsav);
923 DeleteObject( hfont);
925 else
927 r = *rect;
928 InflateRect( &r, -1, -1 );
929 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
930 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
932 return;
935 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
937 got_bitmap:
938 hdcMem = CreateCompatibleDC( hdc );
939 SelectObject( hdcMem, bmp );
941 /* handle fontsize > bitmap_height */
942 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
943 left=rect->left;
944 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
945 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
946 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
947 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
948 DeleteDC( hdcMem );
952 /***********************************************************************
953 * MENU_CalcItemSize
955 * Calculate the size of the menu item and store it in lpitem->rect.
957 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
958 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
960 WCHAR *p;
961 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
962 UINT arrow_bitmap_width;
963 BITMAP bm;
964 INT itemheight;
966 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
967 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
968 (menuBar ? " (MenuBar)" : ""));
970 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
971 arrow_bitmap_width = bm.bmWidth;
973 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
974 if( !menucharsize.cx ) {
975 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
976 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
977 * but it is unlikely an application will depend on that */
978 ODitemheight = HIWORD( GetDialogBaseUnits());
981 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
983 if (lpitem->fType & MF_OWNERDRAW)
985 MEASUREITEMSTRUCT mis;
986 mis.CtlType = ODT_MENU;
987 mis.CtlID = 0;
988 mis.itemID = lpitem->wID;
989 mis.itemData = lpitem->dwItemData;
990 mis.itemHeight = ODitemheight;
991 mis.itemWidth = 0;
992 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
993 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
994 * width of a menufont character to the width of an owner-drawn menu.
996 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
997 if (menuBar) {
998 /* under at least win95 you seem to be given a standard
999 height for the menu and the height value is ignored */
1000 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1001 } else
1002 lpitem->rect.bottom += mis.itemHeight;
1004 TRACE("id=%04lx size=%dx%d\n",
1005 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1006 lpitem->rect.bottom-lpitem->rect.top);
1007 return;
1010 if (lpitem->fType & MF_SEPARATOR)
1012 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1013 if( !menuBar)
1014 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1015 return;
1018 itemheight = 0;
1019 lpitem->xTab = 0;
1021 if (!menuBar) {
1022 if (lpitem->hbmpItem) {
1023 SIZE size;
1025 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1026 /* Keep the size of the bitmap in callback mode to be able
1027 * to draw it correctly */
1028 lpitem->bmpsize = size;
1029 lppop->textOffset = max( lppop->textOffset, size.cx);
1030 lpitem->rect.right += size.cx + 2;
1031 itemheight = size.cy + 2;
1033 if( !(lppop->dwStyle & MNS_NOCHECK))
1034 lpitem->rect.right += check_bitmap_width;
1035 lpitem->rect.right += 4 + menucharsize.cx;
1036 lpitem->xTab = lpitem->rect.right;
1037 lpitem->rect.right += arrow_bitmap_width;
1038 } else if (lpitem->hbmpItem) { /* menuBar */
1039 SIZE size;
1041 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1042 lpitem->bmpsize = size;
1043 lpitem->rect.right += size.cx;
1044 if( lpitem->text) lpitem->rect.right += 2;
1045 itemheight = size.cy;
1048 /* it must be a text item - unless it's the system menu */
1049 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1050 HFONT hfontOld = NULL;
1051 RECT rc = lpitem->rect;
1052 LONG txtheight, txtwidth;
1054 if ( lpitem->fState & MFS_DEFAULT ) {
1055 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1057 if (menuBar) {
1058 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1059 DT_SINGLELINE|DT_CALCRECT);
1060 lpitem->rect.right += rc.right - rc.left;
1061 itemheight = max( max( itemheight, txtheight),
1062 GetSystemMetrics( SM_CYMENU) - 1);
1063 lpitem->rect.right += 2 * menucharsize.cx;
1064 } else {
1065 if ((p = wcschr( lpitem->text, '\t' )) != NULL) {
1066 RECT tmprc = rc;
1067 LONG tmpheight;
1068 int n = (int)( p - lpitem->text);
1069 /* Item contains a tab (only meaningful in popup menus) */
1070 /* get text size before the tab */
1071 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1072 DT_SINGLELINE|DT_CALCRECT);
1073 txtwidth = rc.right - rc.left;
1074 p += 1; /* advance past the Tab */
1075 /* get text size after the tab */
1076 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1077 DT_SINGLELINE|DT_CALCRECT);
1078 lpitem->xTab += txtwidth;
1079 txtheight = max( txtheight, tmpheight);
1080 txtwidth += menucharsize.cx + /* space for the tab */
1081 tmprc.right - tmprc.left; /* space for the short cut */
1082 } else {
1083 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1084 DT_SINGLELINE|DT_CALCRECT);
1085 txtwidth = rc.right - rc.left;
1086 lpitem->xTab += txtwidth;
1088 lpitem->rect.right += 2 + txtwidth;
1089 itemheight = max( itemheight,
1090 max( txtheight + 2, menucharsize.cy + 4));
1092 if (hfontOld) SelectObject (hdc, hfontOld);
1093 } else if( menuBar) {
1094 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1096 lpitem->rect.bottom += itemheight;
1097 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1100 /***********************************************************************
1101 * MENU_PopupMenuCalcSize
1103 * Calculate the size of a popup menu.
1105 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
1107 MENUITEM *lpitem;
1108 HDC hdc;
1109 UINT start, i;
1110 BOOL textandbmp = FALSE, multi_col = FALSE;
1111 int orgX, orgY, maxTab, maxTabWidth;
1113 lppop->Width = lppop->Height = 0;
1114 SetRectEmpty(&lppop->items_rect);
1116 if (lppop->nItems == 0) return;
1117 hdc = GetDC( 0 );
1119 SelectObject( hdc, get_menu_font(FALSE));
1121 start = 0;
1123 lppop->textOffset = 0;
1125 while (start < lppop->nItems)
1127 lpitem = &lppop->items[start];
1128 orgX = lppop->items_rect.right;
1129 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1130 orgX += MENU_COL_SPACE;
1131 orgY = lppop->items_rect.top;
1133 maxTab = maxTabWidth = 0;
1134 /* Parse items until column break or end of menu */
1135 for (i = start; i < lppop->nItems; i++, lpitem++)
1137 if (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1139 multi_col = TRUE;
1140 if (i != start) break;
1143 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1144 lppop->items_rect.right = max( lppop->items_rect.right, lpitem->rect.right );
1145 orgY = lpitem->rect.bottom;
1146 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1148 maxTab = max( maxTab, lpitem->xTab );
1149 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1151 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1154 /* Finish the column (set all items to the largest width found) */
1155 lppop->items_rect.right = max( lppop->items_rect.right, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = lppop->items_rect.right;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->items_rect.bottom = max( lppop->items_rect.bottom, orgY );
1166 /* if none of the items have both text and bitmap then
1167 * the text and bitmaps are all aligned on the left. If there is at
1168 * least one item with both text and bitmap then bitmaps are
1169 * on the left and texts left aligned with the right hand side
1170 * of the bitmaps */
1171 if( !textandbmp) lppop->textOffset = 0;
1173 lppop->nTotalHeight = lppop->items_rect.bottom;
1175 /* space for the border */
1176 OffsetRect(&lppop->items_rect, MENU_MARGIN, MENU_MARGIN);
1177 lppop->Height = lppop->items_rect.bottom + MENU_MARGIN;
1178 lppop->Width = lppop->items_rect.right + MENU_MARGIN;
1180 /* Adjust popup height if it exceeds maximum */
1181 if (lppop->Height >= max_height)
1183 lppop->Height = max_height;
1184 lppop->bScrolling = !multi_col;
1185 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1186 if (lppop->bScrolling)
1188 lppop->items_rect.top = get_scroll_arrow_height(lppop);
1189 lppop->items_rect.bottom = lppop->Height - get_scroll_arrow_height(lppop);
1192 else
1194 lppop->bScrolling = FALSE;
1197 NtUserReleaseDC( 0, hdc );
1201 /***********************************************************************
1202 * MENU_MenuBarCalcSize
1204 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1205 * height is off by 1 pixel which causes lengthy window relocations when
1206 * active document window is maximized/restored.
1208 * Calculate the size of the menu bar.
1210 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1211 LPPOPUPMENU lppop, HWND hwndOwner )
1213 MENUITEM *lpitem;
1214 UINT start, i, helpPos;
1215 int orgX, orgY;
1217 if ((lprect == NULL) || (lppop == NULL)) return;
1218 if (lppop->nItems == 0) return;
1219 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1220 /* Start with a 1 pixel top border.
1221 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1222 SetRect(&lppop->items_rect, 0, 0, lprect->right - lprect->left, 1);
1223 start = 0;
1224 helpPos = ~0U;
1225 lppop->textOffset = 0;
1226 while (start < lppop->nItems)
1228 lpitem = &lppop->items[start];
1229 orgX = lppop->items_rect.left;
1230 orgY = lppop->items_rect.bottom;
1232 /* Parse items until line break or end of menu */
1233 for (i = start; i < lppop->nItems; i++, lpitem++)
1235 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1236 if ((i != start) &&
1237 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1239 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1240 debug_print_menuitem (" item: ", lpitem, "");
1241 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1243 if (lpitem->rect.right > lppop->items_rect.right)
1245 if (i != start) break;
1246 else lpitem->rect.right = lppop->items_rect.right;
1248 lppop->items_rect.bottom = max( lppop->items_rect.bottom, lpitem->rect.bottom );
1249 orgX = lpitem->rect.right;
1252 /* Finish the line (set all items to the largest height found) */
1253 while (start < i) lppop->items[start++].rect.bottom = lppop->items_rect.bottom;
1256 OffsetRect(&lppop->items_rect, lprect->left, lprect->top);
1257 lppop->Width = lppop->items_rect.right - lppop->items_rect.left;
1258 lppop->Height = lppop->items_rect.bottom - lppop->items_rect.top;
1259 lprect->bottom = lppop->items_rect.bottom;
1261 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1262 /* the last item (if several lines, only move the last line) */
1263 if (helpPos == ~0U) return;
1264 lpitem = &lppop->items[lppop->nItems-1];
1265 orgY = lpitem->rect.top;
1266 orgX = lprect->right - lprect->left;
1267 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1268 if (lpitem->rect.top != orgY) break; /* Other line */
1269 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1270 lpitem->rect.left += orgX - lpitem->rect.right;
1271 lpitem->rect.right = orgX;
1272 orgX = lpitem->rect.left;
1276 static void draw_scroll_arrow(HDC hdc, int x, int top, int height, BOOL up, BOOL enabled)
1278 RECT rect, light_rect;
1279 HBRUSH brush = GetSysColorBrush( enabled ? COLOR_BTNTEXT : COLOR_BTNSHADOW );
1280 HBRUSH light = GetSysColorBrush( COLOR_3DLIGHT );
1282 if (!up)
1284 top = top + height;
1285 if (!enabled)
1287 SetRect( &rect, x + 1, top, x + 2, top + 1);
1288 FillRect( hdc, &rect, light );
1290 top--;
1293 SetRect( &rect, x, top, x + 1, top + 1);
1294 while (height--)
1296 FillRect( hdc, &rect, brush );
1297 if (!enabled && !up && height)
1299 SetRect( &light_rect, rect.right, rect.top, rect.right + 2, rect.bottom );
1300 FillRect( hdc, &light_rect, light );
1302 InflateRect( &rect, 1, 0 );
1303 OffsetRect( &rect, 0, up ? 1 : -1 );
1306 if (!enabled && up)
1308 rect.left += 2;
1309 FillRect( hdc, &rect, light );
1313 /***********************************************************************
1314 * MENU_DrawScrollArrows
1316 * Draw scroll arrows.
1318 static void
1319 MENU_DrawScrollArrows(const POPUPMENU *menu, HDC hdc)
1321 UINT full_height = get_scroll_arrow_height( menu );
1322 UINT arrow_height = full_height / 3;
1323 BOOL at_end = menu->nScrollPos + menu->items_rect.bottom - menu->items_rect.top == menu->nTotalHeight;
1325 draw_scroll_arrow( hdc, menu->Width / 3, arrow_height, arrow_height,
1326 TRUE, menu->nScrollPos != 0);
1327 draw_scroll_arrow( hdc, menu->Width / 3, menu->Height - 2 * arrow_height, arrow_height,
1328 FALSE, !at_end );
1332 /***********************************************************************
1333 * draw_popup_arrow
1335 * Draws the popup-menu arrow.
1337 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1338 UINT arrow_bitmap_height)
1340 HDC hdcMem = CreateCompatibleDC( hdc );
1341 HBITMAP hOrigBitmap;
1343 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1344 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1345 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1346 arrow_bitmap_width, arrow_bitmap_height,
1347 hdcMem, 0, 0, SRCCOPY );
1348 SelectObject( hdcMem, hOrigBitmap );
1349 DeleteDC( hdcMem );
1351 /***********************************************************************
1352 * MENU_DrawMenuItem
1354 * Draw a single menu item.
1356 static void MENU_DrawMenuItem( HWND hwnd, POPUPMENU *menu, HWND hwndOwner, HDC hdc,
1357 MENUITEM *lpitem, BOOL menuBar, UINT odaction )
1359 RECT rect, bmprc;
1360 BOOL flat_menu = FALSE;
1361 int bkgnd;
1362 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1363 HRGN old_clip = NULL, clip;
1365 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1367 if (!menuBar) {
1368 BITMAP bmp;
1369 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1370 arrow_bitmap_width = bmp.bmWidth;
1371 arrow_bitmap_height = bmp.bmHeight;
1374 if (lpitem->fType & MF_SYSMENU)
1376 if( !IsIconic(hwnd) )
1377 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1378 return;
1381 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem->rect ) );
1382 rect = lpitem->rect;
1383 MENU_AdjustMenuItemRect( menu, &rect );
1384 if (!IntersectRect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
1385 return;
1387 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1388 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1390 /* Setup colors */
1392 if (lpitem->fState & MF_HILITE)
1394 if(menuBar && !flat_menu) {
1395 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1396 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1397 } else {
1398 if(lpitem->fState & MF_GRAYED)
1399 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1400 else
1401 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1402 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1405 else
1407 if (lpitem->fState & MF_GRAYED)
1408 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1409 else
1410 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1411 SetBkColor( hdc, GetSysColor( bkgnd ) );
1414 old_clip = CreateRectRgn( 0, 0, 0, 0 );
1415 if (GetClipRgn( hdc, old_clip ) <= 0)
1417 DeleteObject( old_clip );
1418 old_clip = NULL;
1420 clip = CreateRectRgnIndirect( &menu->items_rect );
1421 ExtSelectClipRgn( hdc, clip, RGN_AND );
1422 DeleteObject( clip );
1424 if (lpitem->fType & MF_OWNERDRAW)
1427 ** Experimentation under Windows reveals that an owner-drawn
1428 ** menu is given the rectangle which includes the space it requested
1429 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1430 ** and a popup-menu arrow. This is the value of lpitem->rect.
1431 ** Windows will leave all drawing to the application except for
1432 ** the popup-menu arrow. Windows always draws that itself, after
1433 ** the menu owner has finished drawing.
1435 DRAWITEMSTRUCT dis;
1436 COLORREF old_bk, old_text;
1438 dis.CtlType = ODT_MENU;
1439 dis.CtlID = 0;
1440 dis.itemID = lpitem->wID;
1441 dis.itemData = lpitem->dwItemData;
1442 dis.itemState = 0;
1443 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1444 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1445 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1446 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1447 dis.hwndItem = (HWND)menu->obj.handle;
1448 dis.hDC = hdc;
1449 dis.rcItem = rect;
1450 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1451 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1452 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1453 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1454 old_bk = GetBkColor( hdc );
1455 old_text = GetTextColor( hdc );
1456 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1457 /* Draw the popup-menu arrow */
1458 SetBkColor( hdc, old_bk );
1459 SetTextColor( hdc, old_text );
1460 if (lpitem->fType & MF_POPUP)
1461 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1462 arrow_bitmap_height);
1463 goto done;
1466 if (menuBar && (lpitem->fType & MF_SEPARATOR)) goto done;
1468 if (lpitem->fState & MF_HILITE)
1470 if (flat_menu)
1472 InflateRect (&rect, -1, -1);
1473 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1474 InflateRect (&rect, 1, 1);
1475 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1477 else
1479 if(menuBar)
1480 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1481 else
1482 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1485 else
1486 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1488 SetBkMode( hdc, TRANSPARENT );
1490 /* vertical separator */
1491 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1493 HPEN oldPen;
1494 RECT rc = rect;
1496 rc.left -= MENU_COL_SPACE / 2 + 1;
1497 rc.top = 3;
1498 rc.bottom = menu->Height - 3;
1499 if (flat_menu)
1501 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1502 MoveToEx( hdc, rc.left, rc.top, NULL );
1503 LineTo( hdc, rc.left, rc.bottom );
1504 SelectObject( hdc, oldPen );
1506 else
1507 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1510 /* horizontal separator */
1511 if (lpitem->fType & MF_SEPARATOR)
1513 HPEN oldPen;
1514 RECT rc = rect;
1516 InflateRect( &rc, -1, 0 );
1517 rc.top = ( rc.top + rc.bottom) / 2;
1518 if (flat_menu)
1520 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1521 MoveToEx( hdc, rc.left, rc.top, NULL );
1522 LineTo( hdc, rc.right, rc.top );
1523 SelectObject( hdc, oldPen );
1525 else
1526 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1527 goto done;
1530 /* helper lines for debugging */
1531 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1532 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1533 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1534 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1537 if (lpitem->hbmpItem) {
1538 /* calculate the bitmap rectangle in coordinates relative
1539 * to the item rectangle */
1540 if( menuBar) {
1541 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1542 bmprc.left = 3;
1543 else
1544 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1546 else if (menu->dwStyle & MNS_NOCHECK)
1547 bmprc.left = 4;
1548 else if (menu->dwStyle & MNS_CHECKORBMP)
1549 bmprc.left = 2;
1550 else
1551 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1552 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1553 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1554 bmprc.top = 0;
1555 else
1556 bmprc.top = (rect.bottom - rect.top -
1557 lpitem->bmpsize.cy) / 2;
1558 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1561 if (!menuBar)
1563 HBITMAP bm;
1564 INT y = rect.top + rect.bottom;
1565 BOOL checked = FALSE;
1566 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1567 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1568 /* Draw the check mark
1570 * FIXME:
1571 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1573 if (!(menu->dwStyle & MNS_NOCHECK))
1575 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1576 lpitem->hUnCheckBit;
1577 if (bm) /* we have a custom bitmap */
1579 HDC hdcMem = CreateCompatibleDC( hdc );
1581 SelectObject( hdcMem, bm );
1582 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1583 check_bitmap_width, check_bitmap_height,
1584 hdcMem, 0, 0, SRCCOPY );
1585 DeleteDC( hdcMem );
1586 checked = TRUE;
1588 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1590 RECT r;
1591 HBITMAP bm = CreateBitmap( check_bitmap_width,
1592 check_bitmap_height, 1, 1, NULL );
1593 HDC hdcMem = CreateCompatibleDC( hdc );
1595 SelectObject( hdcMem, bm );
1596 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1597 DrawFrameControl( hdcMem, &r, DFC_MENU,
1598 (lpitem->fType & MFT_RADIOCHECK) ?
1599 DFCS_MENUBULLET : DFCS_MENUCHECK );
1600 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1601 hdcMem, 0, 0, SRCCOPY );
1602 DeleteDC( hdcMem );
1603 DeleteObject( bm );
1604 checked = TRUE;
1607 if (lpitem->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
1609 POINT origorg;
1610 /* some applications make this assumption on the DC's origin */
1611 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1612 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1613 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1615 /* Draw the popup-menu arrow */
1616 if (lpitem->fType & MF_POPUP)
1617 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1618 arrow_bitmap_height);
1619 rect.left += 4;
1620 if( !(menu->dwStyle & MNS_NOCHECK))
1621 rect.left += check_bitmap_width;
1622 rect.right -= arrow_bitmap_width;
1624 else if (lpitem->hbmpItem)
1625 { /* Draw the bitmap */
1626 POINT origorg;
1628 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1629 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
1630 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1632 /* process text if present */
1633 if (lpitem->text)
1635 int i;
1636 HFONT hfontOld = 0;
1638 UINT uFormat = (menuBar) ?
1639 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1640 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1642 if( !(menu->dwStyle & MNS_CHECKORBMP))
1643 rect.left += menu->textOffset;
1645 if ( lpitem->fState & MFS_DEFAULT )
1647 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1650 if (menuBar) {
1651 if( lpitem->hbmpItem)
1652 rect.left += lpitem->bmpsize.cx;
1653 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1654 rect.left += menucharsize.cx;
1655 rect.right -= menucharsize.cx;
1658 for (i = 0; lpitem->text[i]; i++)
1659 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1660 break;
1662 if(lpitem->fState & MF_GRAYED)
1664 if (!(lpitem->fState & MF_HILITE) )
1666 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1667 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1668 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1669 --rect.left; --rect.top; --rect.right; --rect.bottom;
1671 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1674 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1676 /* paint the shortcut text */
1677 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1679 if (lpitem->text[i] == '\t')
1681 rect.left = lpitem->xTab;
1682 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1684 else
1686 rect.right = lpitem->xTab;
1687 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1690 if(lpitem->fState & MF_GRAYED)
1692 if (!(lpitem->fState & MF_HILITE) )
1694 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1695 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1696 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1697 --rect.left; --rect.top; --rect.right; --rect.bottom;
1699 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1701 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1704 if (hfontOld)
1705 SelectObject (hdc, hfontOld);
1708 done:
1709 ExtSelectClipRgn( hdc, old_clip, RGN_COPY );
1710 if (old_clip) DeleteObject( old_clip );
1714 /***********************************************************************
1715 * MENU_DrawPopupMenu
1717 * Paint a popup menu.
1719 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1721 HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1722 RECT rect;
1723 POPUPMENU *menu = MENU_GetMenu( hmenu );
1725 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1727 GetClientRect( hwnd, &rect );
1729 if (menu && menu->hbrBack) brush = menu->hbrBack;
1730 if ((hPrevBrush = SelectObject( hdc, brush ))
1731 && SelectObject( hdc, get_menu_font(FALSE) ))
1733 HPEN hPrevPen;
1735 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1737 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1738 if( hPrevPen )
1740 BOOL flat_menu = FALSE;
1742 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1743 if (flat_menu)
1744 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1745 else
1746 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1748 if (menu)
1750 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1751 /* draw menu items */
1752 if (menu->nItems)
1754 MENUITEM *item;
1755 UINT u;
1757 item = menu->items;
1758 for (u = menu->nItems; u > 0; u--, item++)
1759 MENU_DrawMenuItem( hwnd, menu, menu->hwndOwner, hdc,
1760 item, FALSE, ODA_DRAWENTIRE );
1762 /* draw scroll arrows */
1763 if (menu->bScrolling)
1764 MENU_DrawScrollArrows(menu, hdc);
1766 } else
1768 SelectObject( hdc, hPrevBrush );
1773 /***********************************************************************
1774 * MENU_DrawMenuBar
1776 * Paint a menu bar. Returns the height of the menu bar.
1777 * called from [windows/nonclient.c]
1779 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
1781 LPPOPUPMENU lppop;
1782 HMENU hMenu = GetMenu(hwnd);
1784 lppop = MENU_GetMenu( hMenu );
1785 if (lppop == NULL || lprect == NULL)
1787 return GetSystemMetrics(SM_CYMENU);
1790 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1794 /***********************************************************************
1795 * MENU_InitPopup
1797 * Popup menu initialization before WM_ENTERMENULOOP.
1799 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1801 POPUPMENU *menu;
1802 DWORD ex_style = 0;
1804 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1806 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1808 /* store the owner for DrawItem */
1809 if (!IsWindow( hwndOwner ))
1811 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1812 return FALSE;
1814 menu->hwndOwner = hwndOwner;
1816 if (flags & TPM_LAYOUTRTL)
1817 ex_style = WS_EX_LAYOUTRTL;
1819 /* NOTE: In Windows, top menu popup is not owned. */
1820 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1821 WS_POPUP, 0, 0, 0, 0,
1822 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1823 (LPVOID)hmenu );
1824 if( !menu->hWnd ) return FALSE;
1825 return TRUE;
1829 /***********************************************************************
1830 * MENU_ShowPopup
1832 * Display a popup menu.
1834 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1835 INT x, INT y, INT xanchor, INT yanchor )
1837 POPUPMENU *menu;
1838 POINT pt;
1839 HMONITOR monitor;
1840 MONITORINFO info;
1841 UINT max_height;
1843 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1844 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1846 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1847 if (menu->FocusedItem != NO_SELECTED_ITEM)
1849 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1850 menu->FocusedItem = NO_SELECTED_ITEM;
1853 menu->nScrollPos = 0;
1855 /* FIXME: should use item rect */
1856 pt.x = x;
1857 pt.y = y;
1858 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1859 info.cbSize = sizeof(info);
1860 GetMonitorInfoW( monitor, &info );
1862 max_height = info.rcWork.bottom - info.rcWork.top;
1863 if (menu->cyMax)
1864 max_height = min( max_height, menu->cyMax );
1866 MENU_PopupMenuCalcSize( menu, max_height );
1868 /* adjust popup menu pos so that it fits within the desktop */
1870 if (flags & TPM_LAYOUTRTL)
1871 flags ^= TPM_RIGHTALIGN;
1873 if( flags & TPM_RIGHTALIGN ) x -= menu->Width;
1874 if( flags & TPM_CENTERALIGN ) x -= menu->Width / 2;
1876 if( flags & TPM_BOTTOMALIGN ) y -= menu->Height;
1877 if( flags & TPM_VCENTERALIGN ) y -= menu->Height / 2;
1879 if( x + menu->Width > info.rcWork.right)
1881 if( xanchor && x >= menu->Width - xanchor )
1882 x -= menu->Width - xanchor;
1884 if( x + menu->Width > info.rcWork.right)
1885 x = info.rcWork.right - menu->Width;
1887 if( x < info.rcWork.left ) x = info.rcWork.left;
1889 if( y + menu->Height > info.rcWork.bottom)
1891 if( yanchor && y >= menu->Height + yanchor )
1892 y -= menu->Height + yanchor;
1894 if( y + menu->Height > info.rcWork.bottom)
1895 y = info.rcWork.bottom - menu->Height;
1897 if( y < info.rcWork.top ) y = info.rcWork.top;
1899 if (!top_popup) {
1900 top_popup = menu->hWnd;
1901 top_popup_hmenu = hmenu;
1903 /* Display the window */
1905 NtUserSetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
1906 SWP_SHOWWINDOW | SWP_NOACTIVATE );
1907 UpdateWindow( menu->hWnd );
1908 return TRUE;
1912 /***********************************************************************
1913 * MENU_EnsureMenuItemVisible
1915 static void
1916 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1918 if (lppop->bScrolling)
1920 MENUITEM *item = &lppop->items[wIndex];
1921 UINT nOldPos = lppop->nScrollPos;
1922 const RECT *rc = &lppop->items_rect;
1923 UINT scroll_height = rc->bottom - rc->top;
1925 if (item->rect.bottom > lppop->nScrollPos + scroll_height)
1927 lppop->nScrollPos = item->rect.bottom - scroll_height;
1928 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
1930 else if (item->rect.top < lppop->nScrollPos)
1932 lppop->nScrollPos = item->rect.top;
1933 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
1936 /* Invalidate the scroll arrows if necessary */
1937 if (nOldPos != lppop->nScrollPos)
1939 RECT arrow_rect = lppop->items_rect;
1940 if (nOldPos == 0 || lppop->nScrollPos == 0)
1942 arrow_rect.top = 0;
1943 arrow_rect.bottom = lppop->items_rect.top;
1944 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
1946 if (nOldPos + scroll_height == lppop->nTotalHeight ||
1947 lppop->nScrollPos + scroll_height == lppop->nTotalHeight)
1949 arrow_rect.top = lppop->items_rect.bottom;
1950 arrow_rect.bottom = lppop->Height;
1951 InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
1958 /***********************************************************************
1959 * MENU_SelectItem
1961 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1962 BOOL sendMenuSelect, HMENU topmenu )
1964 LPPOPUPMENU lppop;
1965 HDC hdc;
1967 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1969 lppop = MENU_GetMenu( hmenu );
1970 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1972 if (lppop->FocusedItem == wIndex) return;
1973 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1974 else hdc = NtUserGetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1975 if (!top_popup) {
1976 top_popup = lppop->hWnd;
1977 top_popup_hmenu = hmenu;
1980 SelectObject( hdc, get_menu_font(FALSE));
1982 /* Clear previous highlighted item */
1983 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1985 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1986 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[lppop->FocusedItem],
1987 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1990 /* Highlight new item (if any) */
1991 lppop->FocusedItem = wIndex;
1992 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1994 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1995 lppop->items[wIndex].fState |= MF_HILITE;
1996 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1997 MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[wIndex],
1998 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2000 if (sendMenuSelect)
2002 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2003 SendMessageW( hwndOwner, WM_MENUSELECT,
2004 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2005 ip->fType | ip->fState |
2006 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2009 else if (sendMenuSelect) {
2010 if(topmenu){
2011 int pos;
2012 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2013 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2014 MENUITEM *ip = &ptm->items[pos];
2015 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2016 ip->fType | ip->fState |
2017 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2021 NtUserReleaseDC( lppop->hWnd, hdc );
2025 /***********************************************************************
2026 * MENU_MoveSelection
2028 * Moves currently selected item according to the offset parameter.
2029 * If there is no selection then it should select the last item if
2030 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2032 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2034 INT i;
2035 POPUPMENU *menu;
2037 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2039 menu = MENU_GetMenu( hmenu );
2040 if ((!menu) || (!menu->items)) return;
2042 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2044 if( menu->nItems == 1 ) return; else
2045 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2046 ; i += offset)
2047 if (!(menu->items[i].fType & MF_SEPARATOR))
2049 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2050 return;
2054 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2055 i >= 0 && i < menu->nItems ; i += offset)
2056 if (!(menu->items[i].fType & MF_SEPARATOR))
2058 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2059 return;
2064 /**********************************************************************
2065 * MENU_ParseResource
2067 * Parse a standard menu resource and add items to the menu.
2068 * Return a pointer to the end of the resource.
2070 * NOTE: flags is equivalent to the mtOption field
2072 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2074 WORD flags, id = 0;
2075 LPCWSTR str;
2076 BOOL end_flag;
2080 flags = GET_WORD(res);
2081 end_flag = flags & MF_END;
2082 /* Remove MF_END because it has the same value as MF_HILITE */
2083 flags &= ~MF_END;
2084 res += sizeof(WORD);
2085 if (!(flags & MF_POPUP))
2087 id = GET_WORD(res);
2088 res += sizeof(WORD);
2090 str = (LPCWSTR)res;
2091 res += (lstrlenW(str) + 1) * sizeof(WCHAR);
2092 if (flags & MF_POPUP)
2094 HMENU hSubMenu = CreatePopupMenu();
2095 if (!hSubMenu) return NULL;
2096 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2097 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2099 else /* Not a popup */
2101 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2103 } while (!end_flag);
2104 return res;
2108 /**********************************************************************
2109 * MENUEX_ParseResource
2111 * Parse an extended menu resource and add items to the menu.
2112 * Return a pointer to the end of the resource.
2114 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2116 WORD resinfo;
2117 do {
2118 MENUITEMINFOW mii;
2120 mii.cbSize = sizeof(mii);
2121 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2122 mii.fType = GET_DWORD(res);
2123 res += sizeof(DWORD);
2124 mii.fState = GET_DWORD(res);
2125 res += sizeof(DWORD);
2126 mii.wID = GET_DWORD(res);
2127 res += sizeof(DWORD);
2128 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2129 res += sizeof(WORD);
2130 /* Align the text on a word boundary. */
2131 res += (~((UINT_PTR)res - 1)) & 1;
2132 mii.dwTypeData = (LPWSTR) res;
2133 res += (1 + lstrlenW(mii.dwTypeData)) * sizeof(WCHAR);
2134 /* Align the following fields on a dword boundary. */
2135 res += (~((UINT_PTR)res - 1)) & 3;
2137 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2138 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2140 if (resinfo & 1) { /* Pop-up? */
2141 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2142 res += sizeof(DWORD);
2143 mii.hSubMenu = CreatePopupMenu();
2144 if (!mii.hSubMenu)
2145 return NULL;
2146 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2147 NtUserDestroyMenu( mii.hSubMenu );
2148 return NULL;
2150 mii.fMask |= MIIM_SUBMENU;
2151 mii.fType |= MF_POPUP;
2153 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2155 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2156 mii.wID, mii.fType);
2157 mii.fType |= MF_SEPARATOR;
2159 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2160 } while (!(resinfo & MF_END));
2161 return res;
2165 /***********************************************************************
2166 * MENU_GetSubPopup
2168 * Return the handle of the selected sub-popup menu (if any).
2170 static HMENU MENU_GetSubPopup( HMENU hmenu )
2172 POPUPMENU *menu;
2173 MENUITEM *item;
2175 menu = MENU_GetMenu( hmenu );
2177 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2179 item = &menu->items[menu->FocusedItem];
2180 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2181 return item->hSubMenu;
2182 return 0;
2186 /***********************************************************************
2187 * MENU_HideSubPopups
2189 * Hide the sub-popup menus of this menu.
2191 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2192 BOOL sendMenuSelect, UINT wFlags )
2194 POPUPMENU *menu = MENU_GetMenu( hmenu );
2196 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2198 if (menu && top_popup)
2200 HMENU hsubmenu;
2201 POPUPMENU *submenu;
2202 MENUITEM *item;
2204 if (menu->FocusedItem != NO_SELECTED_ITEM)
2206 item = &menu->items[menu->FocusedItem];
2207 if (!(item->fType & MF_POPUP) ||
2208 !(item->fState & MF_MOUSESELECT)) return;
2209 item->fState &= ~MF_MOUSESELECT;
2210 hsubmenu = item->hSubMenu;
2211 } else return;
2213 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2214 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2215 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2216 NtUserDestroyWindow( submenu->hWnd );
2217 submenu->hWnd = 0;
2219 if (!(wFlags & TPM_NONOTIFY))
2220 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2221 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2226 /***********************************************************************
2227 * MENU_ShowSubPopup
2229 * Display the sub-menu of the selected item of this menu.
2230 * Return the handle of the submenu, or hmenu if no submenu to display.
2232 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2233 BOOL selectFirst, UINT wFlags )
2235 RECT rect;
2236 POPUPMENU *menu;
2237 MENUITEM *item;
2238 HDC hdc;
2240 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2242 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2244 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2246 item = &menu->items[menu->FocusedItem];
2247 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2248 return hmenu;
2250 /* message must be sent before using item,
2251 because nearly everything may be changed by the application ! */
2253 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2254 if (!(wFlags & TPM_NONOTIFY))
2255 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2256 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2258 item = &menu->items[menu->FocusedItem];
2259 rect = item->rect;
2261 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2262 if (!(item->fState & MF_HILITE))
2264 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2265 else hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2267 SelectObject( hdc, get_menu_font(FALSE));
2269 item->fState |= MF_HILITE;
2270 MENU_DrawMenuItem( menu->hWnd, menu, hwndOwner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2271 NtUserReleaseDC( menu->hWnd, hdc );
2273 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2274 item->rect = rect;
2276 item->fState |= MF_MOUSESELECT;
2278 if (IS_SYSTEM_MENU(menu))
2280 MENU_InitSysMenuPopup(item->hSubMenu,
2281 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2282 GetClassLongW( menu->hWnd, GCL_STYLE));
2284 NC_GetSysPopupPos( menu->hWnd, &rect );
2285 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2286 rect.top = rect.bottom;
2287 rect.right = GetSystemMetrics(SM_CXSIZE);
2288 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2290 else
2292 RECT item_rect = item->rect;
2294 MENU_AdjustMenuItemRect(menu, &item_rect);
2295 GetWindowRect( menu->hWnd, &rect );
2297 if (menu->wFlags & MF_POPUP)
2299 /* The first item in the popup menu has to be at the
2300 same y position as the focused menu item */
2301 if (wFlags & TPM_LAYOUTRTL)
2302 rect.left += GetSystemMetrics(SM_CXBORDER);
2303 else
2304 rect.left += item_rect.right - GetSystemMetrics(SM_CXBORDER);
2305 rect.top += item_rect.top - MENU_MARGIN;
2306 rect.right = item_rect.left - item_rect.right + GetSystemMetrics(SM_CXBORDER);
2307 rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
2309 else
2311 if (wFlags & TPM_LAYOUTRTL)
2312 rect.left = rect.right - item_rect.left;
2313 else
2314 rect.left += item_rect.left;
2315 rect.top += item_rect.bottom;
2316 rect.right = item_rect.right - item_rect.left;
2317 rect.bottom = item_rect.bottom - item_rect.top;
2321 /* use default alignment for submenus */
2322 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2324 MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
2326 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2327 rect.left, rect.top, rect.right, rect.bottom );
2328 if (selectFirst)
2329 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2330 return item->hSubMenu;
2335 /**********************************************************************
2336 * MENU_IsMenuActive
2338 HWND MENU_IsMenuActive(void)
2340 return top_popup;
2343 /**********************************************************************
2344 * MENU_EndMenu
2346 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2348 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2350 void MENU_EndMenu( HWND hwnd )
2352 POPUPMENU *menu;
2353 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2354 if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2357 /***********************************************************************
2358 * MENU_PtMenu
2360 * Walks menu chain trying to find a menu pt maps to.
2362 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2364 POPUPMENU *menu = MENU_GetMenu( hMenu );
2365 UINT item = menu->FocusedItem;
2366 HMENU ret;
2368 /* try subpopup first (if any) */
2369 ret = (item != NO_SELECTED_ITEM &&
2370 (menu->items[item].fType & MF_POPUP) &&
2371 (menu->items[item].fState & MF_MOUSESELECT))
2372 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2374 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2376 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2377 if( menu->wFlags & MF_POPUP )
2379 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2381 else if (ht == HTSYSMENU)
2382 ret = get_win_sys_menu( menu->hWnd );
2383 else if (ht == HTMENU)
2384 ret = GetMenu( menu->hWnd );
2386 return ret;
2389 /***********************************************************************
2390 * MENU_ExecFocusedItem
2392 * Execute a menu item (for instance when user pressed Enter).
2393 * Return the wID of the executed item. Otherwise, -1 indicating
2394 * that no menu item was executed, -2 if a popup is shown;
2395 * Have to receive the flags for the TrackPopupMenu options to avoid
2396 * sending unwanted message.
2399 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2401 MENUITEM *item;
2402 POPUPMENU *menu = MENU_GetMenu( hMenu );
2404 TRACE("%p hmenu=%p\n", pmt, hMenu);
2406 if (!menu || !menu->nItems ||
2407 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2409 item = &menu->items[menu->FocusedItem];
2411 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2413 if (!(item->fType & MF_POPUP))
2415 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2417 /* If TPM_RETURNCMD is set you return the id, but
2418 do not send a message to the owner */
2419 if(!(wFlags & TPM_RETURNCMD))
2421 if( menu->wFlags & MF_SYSMENU )
2422 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2423 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2424 else
2426 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2427 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2429 if (dwStyle & MNS_NOTIFYBYPOS)
2430 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2431 (LPARAM)hMenu);
2432 else
2433 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2436 return item->wID;
2439 else
2441 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2442 return -2;
2445 return -1;
2448 /***********************************************************************
2449 * MENU_SwitchTracking
2451 * Helper function for menu navigation routines.
2453 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2455 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2456 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2458 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2460 if( pmt->hTopMenu != hPtMenu &&
2461 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2463 /* both are top level menus (system and menu-bar) */
2464 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2465 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2466 pmt->hTopMenu = hPtMenu;
2468 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2469 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2473 /***********************************************************************
2474 * MENU_ButtonDown
2476 * Return TRUE if we can go on with menu tracking.
2478 static BOOL MENU_ButtonDown( MTRACKER* pmt, UINT message, HMENU hPtMenu, UINT wFlags )
2480 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2482 if (hPtMenu)
2484 UINT pos;
2485 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2486 enum hittest ht = ht_item;
2488 if( IS_SYSTEM_MENU(ptmenu) )
2490 if (message == WM_LBUTTONDBLCLK) return FALSE;
2491 pos = 0;
2493 else
2494 ht = MENU_FindItemByCoords( ptmenu, pmt->pt, &pos );
2496 if (pos != NO_SELECTED_ITEM)
2498 if (ptmenu->FocusedItem != pos)
2499 MENU_SwitchTracking( pmt, hPtMenu, pos, wFlags );
2501 /* If the popup menu is not already "popped" */
2502 if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
2503 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2506 /* A click on an item or anywhere on a popup keeps tracking going */
2507 if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
2508 return TRUE;
2510 return FALSE;
2513 /***********************************************************************
2514 * MENU_ButtonUp
2516 * Return the value of MENU_ExecFocusedItem if
2517 * the selected item was not a popup. Else open the popup.
2518 * A -1 return value indicates that we go on with menu tracking.
2521 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2523 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2525 if (hPtMenu)
2527 UINT pos;
2528 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2530 if( IS_SYSTEM_MENU(ptmenu) )
2531 pos = 0;
2532 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &pos ) != ht_item)
2533 pos = NO_SELECTED_ITEM;
2535 if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
2537 debug_print_menuitem ("FocusedItem: ", &ptmenu->items[pos], "");
2539 if (!(ptmenu->items[pos].fType & MF_POPUP))
2541 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2542 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2543 return executedMenuId;
2546 /* If we are dealing with the menu bar */
2547 /* and this is a click on an already "popped" item: */
2548 /* Stop the menu tracking and close the opened submenus */
2549 if(((pmt->hTopMenu == hPtMenu) || IS_SYSTEM_MENU(ptmenu)) && (pmt->trackFlags & TF_RCVD_BTN_UP))
2550 return 0;
2552 if( GetMenu(ptmenu->hWnd) == hPtMenu || IS_SYSTEM_MENU(ptmenu) )
2554 if (pos == NO_SELECTED_ITEM) return 0;
2555 pmt->trackFlags |= TF_RCVD_BTN_UP;
2558 return -1;
2562 /***********************************************************************
2563 * MENU_MouseMove
2565 * Return TRUE if we can go on with menu tracking.
2567 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2569 UINT id = NO_SELECTED_ITEM;
2570 POPUPMENU *ptmenu = NULL;
2572 if( hPtMenu )
2574 ptmenu = MENU_GetMenu( hPtMenu );
2575 if( IS_SYSTEM_MENU(ptmenu) )
2576 id = 0;
2577 else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &id ) != ht_item)
2578 id = NO_SELECTED_ITEM;
2581 if( id == NO_SELECTED_ITEM )
2583 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2584 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2587 else if( ptmenu->FocusedItem != id )
2589 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2590 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2592 return TRUE;
2596 /***********************************************************************
2597 * MENU_DoNextMenu
2599 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2601 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2603 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2604 BOOL atEnd = FALSE;
2606 /* When skipping left, we need to do something special after the
2607 first menu. */
2608 if (vk == VK_LEFT && menu->FocusedItem == 0)
2610 atEnd = TRUE;
2612 /* When skipping right, for the non-system menu, we need to
2613 handle the last non-special menu item (ie skip any window
2614 icons such as MDI maximize, restore or close) */
2615 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2617 UINT i = menu->FocusedItem + 1;
2618 while (i < menu->nItems) {
2619 if ((menu->items[i].wID >= SC_SIZE &&
2620 menu->items[i].wID <= SC_RESTORE)) {
2621 i++;
2622 } else break;
2624 if (i == menu->nItems) {
2625 atEnd = TRUE;
2628 /* When skipping right, we need to cater for the system menu */
2629 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2631 if (menu->FocusedItem == (menu->nItems - 1)) {
2632 atEnd = TRUE;
2636 if( atEnd )
2638 MDINEXTMENU next_menu;
2639 HMENU hNewMenu;
2640 HWND hNewWnd;
2641 UINT id = 0;
2643 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2644 next_menu.hmenuNext = 0;
2645 next_menu.hwndNext = 0;
2646 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2648 TRACE("%p [%p] -> %p [%p]\n",
2649 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2651 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2653 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2654 hNewWnd = pmt->hOwnerWnd;
2655 if( IS_SYSTEM_MENU(menu) )
2657 /* switch to the menu bar */
2659 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2661 if( vk == VK_LEFT )
2663 menu = MENU_GetMenu( hNewMenu );
2664 id = menu->nItems - 1;
2666 /* Skip backwards over any system predefined icons,
2667 eg. MDI close, restore etc icons */
2668 while ((id > 0) &&
2669 (menu->items[id].wID >= SC_SIZE &&
2670 menu->items[id].wID <= SC_RESTORE)) id--;
2673 else if (style & WS_SYSMENU )
2675 /* switch to the system menu */
2676 hNewMenu = get_win_sys_menu( hNewWnd );
2678 else return FALSE;
2680 else /* application returned a new menu to switch to */
2682 hNewMenu = next_menu.hmenuNext;
2683 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2685 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2687 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2689 if (style & WS_SYSMENU &&
2690 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2692 /* get the real system menu */
2693 hNewMenu = get_win_sys_menu(hNewWnd);
2695 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2697 /* FIXME: Not sure what to do here;
2698 * perhaps try to track hNewMenu as a popup? */
2700 TRACE(" -- got confused.\n");
2701 return FALSE;
2704 else return FALSE;
2707 if( hNewMenu != pmt->hTopMenu )
2709 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2710 FALSE, 0 );
2711 if( pmt->hCurrentMenu != pmt->hTopMenu )
2712 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2715 if( hNewWnd != pmt->hOwnerWnd )
2717 pmt->hOwnerWnd = hNewWnd;
2718 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2721 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2722 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2724 return TRUE;
2726 return FALSE;
2729 /***********************************************************************
2730 * MENU_SuspendPopup
2732 * The idea is not to show the popup if the next input message is
2733 * going to hide it anyway.
2735 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT uMsg )
2737 MSG msg;
2739 msg.hwnd = pmt->hOwnerWnd;
2741 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2742 pmt->trackFlags |= TF_SKIPREMOVE;
2744 switch( uMsg )
2746 case WM_KEYDOWN:
2747 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2748 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2750 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2751 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2752 if( msg.message == WM_KEYDOWN &&
2753 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2755 pmt->trackFlags |= TF_SUSPENDPOPUP;
2756 return TRUE;
2759 break;
2762 /* failures go through this */
2763 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2764 return FALSE;
2767 /***********************************************************************
2768 * MENU_KeyEscape
2770 * Handle a VK_ESCAPE key event in a menu.
2772 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2774 BOOL bEndMenu = TRUE;
2776 if (pmt->hCurrentMenu != pmt->hTopMenu)
2778 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2780 if (menu->wFlags & MF_POPUP)
2782 HMENU hmenutmp, hmenuprev;
2784 hmenuprev = hmenutmp = pmt->hTopMenu;
2786 /* close topmost popup */
2787 while (hmenutmp != pmt->hCurrentMenu)
2789 hmenuprev = hmenutmp;
2790 hmenutmp = MENU_GetSubPopup( hmenuprev );
2793 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2794 pmt->hCurrentMenu = hmenuprev;
2795 bEndMenu = FALSE;
2799 return bEndMenu;
2802 /***********************************************************************
2803 * MENU_KeyLeft
2805 * Handle a VK_LEFT key event in a menu.
2807 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags, UINT msg )
2809 POPUPMENU *menu;
2810 HMENU hmenutmp, hmenuprev;
2811 UINT prevcol;
2813 hmenuprev = hmenutmp = pmt->hTopMenu;
2814 menu = MENU_GetMenu( hmenutmp );
2816 /* Try to move 1 column left (if possible) */
2817 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2818 NO_SELECTED_ITEM ) {
2820 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2821 prevcol, TRUE, 0 );
2822 return;
2825 /* close topmost popup */
2826 while (hmenutmp != pmt->hCurrentMenu)
2828 hmenuprev = hmenutmp;
2829 hmenutmp = MENU_GetSubPopup( hmenuprev );
2832 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2833 pmt->hCurrentMenu = hmenuprev;
2835 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2837 /* move menu bar selection if no more popups are left */
2839 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2840 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2842 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2844 /* A sublevel menu was displayed - display the next one
2845 * unless there is another displacement coming up */
2847 if( !MENU_SuspendPopup( pmt, msg ) )
2848 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2849 pmt->hTopMenu, TRUE, wFlags);
2855 /***********************************************************************
2856 * MENU_KeyRight
2858 * Handle a VK_RIGHT key event in a menu.
2860 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags, UINT msg )
2862 HMENU hmenutmp;
2863 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2864 UINT nextcol;
2866 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2867 pmt->hCurrentMenu,
2868 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2869 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2871 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2873 /* If already displaying a popup, try to display sub-popup */
2875 hmenutmp = pmt->hCurrentMenu;
2876 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2878 /* if subpopup was displayed then we are done */
2879 if (hmenutmp != pmt->hCurrentMenu) return;
2882 /* Check to see if there's another column */
2883 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2884 NO_SELECTED_ITEM ) {
2885 TRACE("Going to %d.\n", nextcol );
2886 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2887 nextcol, TRUE, 0 );
2888 return;
2891 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2893 if( pmt->hCurrentMenu != pmt->hTopMenu )
2895 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2896 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2897 } else hmenutmp = 0;
2899 /* try to move to the next item */
2900 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2901 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2903 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2904 if( !MENU_SuspendPopup( pmt, msg ) )
2905 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2906 pmt->hTopMenu, TRUE, wFlags);
2910 static void CALLBACK release_capture( BOOL __normal )
2912 set_capture_window( 0, GUI_INMENUMODE, NULL );
2915 /***********************************************************************
2916 * MENU_TrackMenu
2918 * Menu tracking code.
2920 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2921 HWND hwnd, const RECT *lprect )
2923 MSG msg;
2924 POPUPMENU *menu;
2925 BOOL fRemove;
2926 INT executedMenuId = -1;
2927 MTRACKER mt;
2928 BOOL enterIdleSent = FALSE;
2929 HWND capture_win;
2931 mt.trackFlags = 0;
2932 mt.hCurrentMenu = hmenu;
2933 mt.hTopMenu = hmenu;
2934 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2935 mt.pt.x = x;
2936 mt.pt.y = y;
2938 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2939 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2941 if (!(menu = MENU_GetMenu( hmenu )))
2943 WARN("Invalid menu handle %p\n", hmenu);
2944 SetLastError(ERROR_INVALID_MENU_HANDLE);
2945 return FALSE;
2948 if (wFlags & TPM_BUTTONDOWN)
2950 /* Get the result in order to start the tracking or not */
2951 fRemove = MENU_ButtonDown( &mt, WM_LBUTTONDOWN, hmenu, wFlags );
2952 fEndMenu = !fRemove;
2955 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2957 /* owner may not be visible when tracking a popup, so use the menu itself */
2958 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
2959 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
2961 if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
2962 return FALSE;
2964 __TRY while (!fEndMenu)
2966 menu = MENU_GetMenu( mt.hCurrentMenu );
2967 if (!menu) /* sometimes happens if I do a window manager close */
2968 break;
2970 /* we have to keep the message in the queue until it's
2971 * clear that menu loop is not over yet. */
2973 for (;;)
2975 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2977 if (!NtUserCallMsgFilter( &msg, MSGF_MENU )) break;
2978 /* remove the message from the queue */
2979 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2981 else
2983 if (!enterIdleSent)
2985 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
2986 enterIdleSent = TRUE;
2987 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2989 WaitMessage();
2993 /* check if EndMenu() tried to cancel us, by posting this message */
2994 if(msg.message == WM_CANCELMODE)
2996 /* we are now out of the loop */
2997 fEndMenu = TRUE;
2999 /* remove the message from the queue */
3000 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3002 /* break out of internal loop, ala ESCAPE */
3003 break;
3006 mt.pt = msg.pt;
3008 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3009 enterIdleSent=FALSE;
3011 fRemove = FALSE;
3012 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3015 * Use the mouse coordinates in lParam instead of those in the MSG
3016 * struct to properly handle synthetic messages. They are already
3017 * in screen coordinates.
3019 mt.pt.x = (short)LOWORD(msg.lParam);
3020 mt.pt.y = (short)HIWORD(msg.lParam);
3022 /* Find a menu for this mouse event */
3023 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3025 switch(msg.message)
3027 /* no WM_NC... messages in captured state */
3029 case WM_RBUTTONDBLCLK:
3030 case WM_RBUTTONDOWN:
3031 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3032 /* fall through */
3033 case WM_LBUTTONDBLCLK:
3034 case WM_LBUTTONDOWN:
3035 /* If the message belongs to the menu, removes it from the queue */
3036 /* Else, end menu tracking */
3037 fRemove = MENU_ButtonDown( &mt, msg.message, hmenu, wFlags );
3038 fEndMenu = !fRemove;
3039 break;
3041 case WM_RBUTTONUP:
3042 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3043 /* fall through */
3044 case WM_LBUTTONUP:
3045 /* Check if a menu was selected by the mouse */
3046 if (hmenu)
3048 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3049 TRACE("executedMenuId %d\n", executedMenuId);
3051 /* End the loop if executedMenuId is an item ID */
3052 /* or if the job was done (executedMenuId = 0). */
3053 fEndMenu = fRemove = (executedMenuId != -1);
3055 /* No menu was selected by the mouse */
3056 /* if the function was called by TrackPopupMenu, continue
3057 with the menu tracking. If not, stop it */
3058 else
3059 fEndMenu = !(wFlags & TPM_POPUPMENU);
3061 break;
3063 case WM_MOUSEMOVE:
3064 /* the selected menu item must be changed every time */
3065 /* the mouse moves. */
3067 if (hmenu)
3068 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3070 } /* switch(msg.message) - mouse */
3072 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3074 fRemove = TRUE; /* Keyboard messages are always removed */
3075 switch(msg.message)
3077 case WM_KEYDOWN:
3078 case WM_SYSKEYDOWN:
3079 switch(msg.wParam)
3081 case VK_MENU:
3082 case VK_F10:
3083 fEndMenu = TRUE;
3084 break;
3086 case VK_HOME:
3087 case VK_END:
3088 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3089 NO_SELECTED_ITEM, FALSE, 0 );
3090 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3091 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3092 break;
3094 case VK_UP:
3095 case VK_DOWN: /* If on menu bar, pull-down the menu */
3097 menu = MENU_GetMenu( mt.hCurrentMenu );
3098 if (!(menu->wFlags & MF_POPUP))
3099 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3100 else /* otherwise try to move selection */
3101 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3102 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3103 break;
3105 case VK_LEFT:
3106 MENU_KeyLeft( &mt, wFlags, msg.message );
3107 break;
3109 case VK_RIGHT:
3110 MENU_KeyRight( &mt, wFlags, msg.message );
3111 break;
3113 case VK_ESCAPE:
3114 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3115 break;
3117 case VK_F1:
3119 HELPINFO hi;
3120 hi.cbSize = sizeof(HELPINFO);
3121 hi.iContextType = HELPINFO_MENUITEM;
3122 if (menu->FocusedItem == NO_SELECTED_ITEM)
3123 hi.iCtrlId = 0;
3124 else
3125 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3126 hi.hItemHandle = hmenu;
3127 hi.dwContextId = menu->dwContextHelpID;
3128 hi.MousePos = msg.pt;
3129 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3130 break;
3133 default:
3134 TranslateMessage( &msg );
3135 break;
3137 break; /* WM_KEYDOWN */
3139 case WM_CHAR:
3140 case WM_SYSCHAR:
3142 UINT pos;
3144 if (msg.wParam == '\r' || msg.wParam == ' ')
3146 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3147 fEndMenu = (executedMenuId != -2);
3149 break;
3152 /* Hack to avoid control chars. */
3153 /* We will find a better way real soon... */
3154 if (msg.wParam < 32) break;
3156 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3157 LOWORD(msg.wParam), FALSE );
3158 if (pos == (UINT)-2) fEndMenu = TRUE;
3159 else if (pos == (UINT)-1) MessageBeep(0);
3160 else
3162 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3163 TRUE, 0 );
3164 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3165 fEndMenu = (executedMenuId != -2);
3168 break;
3169 } /* switch(msg.message) - kbd */
3171 else
3173 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3174 DispatchMessageW( &msg );
3175 continue;
3178 if (!fEndMenu) fRemove = TRUE;
3180 /* finally remove message from the queue */
3182 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3183 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3184 else mt.trackFlags &= ~TF_SKIPREMOVE;
3186 __FINALLY( release_capture )
3188 /* If dropdown is still painted and the close box is clicked on
3189 then the menu will be destroyed as part of the DispatchMessage above.
3190 This will then invalidate the menu handle in mt.hTopMenu. We should
3191 check for this first. */
3192 if( IsMenu( mt.hTopMenu ) )
3194 menu = MENU_GetMenu( mt.hTopMenu );
3196 if( IsWindow( mt.hOwnerWnd ) )
3198 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3200 if (menu && (menu->wFlags & MF_POPUP))
3202 NtUserDestroyWindow( menu->hWnd );
3203 menu->hWnd = 0;
3205 if (!(wFlags & TPM_NONOTIFY))
3206 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3207 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3209 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3210 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3214 SetLastError( ERROR_SUCCESS );
3215 /* The return value is only used by TrackPopupMenu */
3216 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3217 if (executedMenuId == -1) executedMenuId = 0;
3218 return executedMenuId;
3221 /***********************************************************************
3222 * MENU_InitTracking
3224 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3226 POPUPMENU *menu;
3228 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3230 NtUserHideCaret( 0 );
3232 if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
3234 /* This makes the menus of applications built with Delphi work.
3235 * It also enables menus to be displayed in more than one window,
3236 * but there are some bugs left that need to be fixed in this case.
3238 if (!bPopup) menu->hWnd = hWnd;
3239 if (!top_popup)
3241 top_popup = menu->hWnd;
3242 top_popup_hmenu = hMenu;
3245 fEndMenu = FALSE;
3247 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3248 if (!(wFlags & TPM_NONOTIFY))
3249 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3251 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3253 if (!(wFlags & TPM_NONOTIFY))
3255 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3256 /* If an app changed/recreated menu bar entries in WM_INITMENU
3257 * menu sizes will be recalculated once the menu created/shown.
3261 return TRUE;
3264 /***********************************************************************
3265 * MENU_ExitTracking
3267 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3269 TRACE("hwnd=%p\n", hWnd);
3271 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3272 NtUserShowCaret( 0 );
3273 top_popup = 0;
3274 top_popup_hmenu = NULL;
3275 return TRUE;
3278 /***********************************************************************
3279 * MENU_TrackMouseMenuBar
3281 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3283 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3285 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3286 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3288 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3290 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3291 if (IsMenu(hMenu))
3293 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3295 /* fetch the window menu again, it may have changed */
3296 hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3297 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3298 MENU_ExitTracking(hWnd, FALSE);
3303 /***********************************************************************
3304 * MENU_TrackKbdMenuBar
3306 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3308 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3310 UINT uItem = NO_SELECTED_ITEM;
3311 HMENU hTrackMenu;
3312 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3314 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3316 /* find window that has a menu */
3318 while (is_win_menu_disallowed(hwnd))
3319 if (!(hwnd = NtUserGetAncestor( hwnd, GA_PARENT ))) return;
3321 /* check if we have to track a system menu */
3323 hTrackMenu = GetMenu( hwnd );
3324 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3326 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3327 hTrackMenu = get_win_sys_menu( hwnd );
3328 uItem = 0;
3329 wParam |= HTSYSMENU; /* prevent item lookup */
3331 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3333 if (!IsMenu( hTrackMenu )) return;
3335 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3337 /* fetch the window menu again, it may have changed */
3338 hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
3340 if( wChar && wChar != ' ' )
3342 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3343 if ( uItem >= (UINT)(-2) )
3345 if( uItem == (UINT)(-1) ) MessageBeep(0);
3346 /* schedule end of menu tracking */
3347 wFlags |= TF_ENDMENU;
3348 goto track_menu;
3352 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3354 if (!(wParam & HTSYSMENU) || wChar == ' ')
3356 if( uItem == NO_SELECTED_ITEM )
3357 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3358 else
3359 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3362 track_menu:
3363 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3364 MENU_ExitTracking( hwnd, FALSE );
3367 /**********************************************************************
3368 * TrackPopupMenuEx (USER32.@)
3370 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3371 HWND hWnd, LPTPMPARAMS lpTpm )
3373 POPUPMENU *menu;
3374 BOOL ret = FALSE;
3376 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3377 hMenu, wFlags, x, y, hWnd, lpTpm,
3378 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3380 /* Parameter check */
3381 /* FIXME: this check is performed several times, here and in the called
3382 functions. That could be optimized */
3383 if (!(menu = MENU_GetMenu( hMenu )))
3385 SetLastError( ERROR_INVALID_MENU_HANDLE );
3386 return FALSE;
3389 if (IsWindow(menu->hWnd))
3391 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3392 return FALSE;
3395 if (MENU_InitPopup( hWnd, hMenu, wFlags ))
3397 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3399 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3400 if (!(wFlags & TPM_NONOTIFY))
3401 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3403 if (menu->wFlags & MF_SYSMENU)
3404 MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
3405 GetClassLongW( hWnd, GCL_STYLE));
3407 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3408 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3409 lpTpm ? &lpTpm->rcExclude : NULL );
3410 MENU_ExitTracking(hWnd, TRUE);
3412 if (menu->hWnd)
3414 NtUserDestroyWindow( menu->hWnd );
3415 menu->hWnd = 0;
3417 if (!(wFlags & TPM_NONOTIFY))
3418 SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
3419 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3421 SetLastError(0);
3424 return ret;
3427 /**********************************************************************
3428 * TrackPopupMenu (USER32.@)
3430 * Like the win32 API, the function return the command ID only if the
3431 * flag TPM_RETURNCMD is on.
3434 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3435 INT nReserved, HWND hWnd, const RECT *lpRect )
3437 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3440 /***********************************************************************
3441 * PopupMenuWndProc
3443 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3445 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3447 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3449 switch(message)
3451 case WM_CREATE:
3453 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3454 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3455 return 0;
3458 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3459 return MA_NOACTIVATE;
3461 case WM_PAINT:
3463 PAINTSTRUCT ps;
3464 NtUserBeginPaint( hwnd, &ps );
3465 MENU_DrawPopupMenu( hwnd, ps.hdc,
3466 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3467 NtUserEndPaint( hwnd, &ps );
3468 return 0;
3471 case WM_PRINTCLIENT:
3473 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3474 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3475 return 0;
3478 case WM_ERASEBKGND:
3479 return 1;
3481 case WM_DESTROY:
3482 /* zero out global pointer in case resident popup window was destroyed. */
3483 if (hwnd == top_popup) {
3484 top_popup = 0;
3485 top_popup_hmenu = NULL;
3487 break;
3489 case WM_SHOWWINDOW:
3491 if( wParam )
3493 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3495 else
3496 SetWindowLongPtrW( hwnd, 0, 0 );
3497 break;
3499 case MN_GETHMENU:
3500 return GetWindowLongPtrW( hwnd, 0 );
3502 default:
3503 return DefWindowProcW( hwnd, message, wParam, lParam );
3505 return 0;
3509 /***********************************************************************
3510 * MENU_GetMenuBarHeight
3512 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3514 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3515 INT orgX, INT orgY )
3517 HDC hdc;
3518 RECT rectBar;
3519 LPPOPUPMENU lppop;
3521 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3523 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3525 hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3526 SelectObject( hdc, get_menu_font(FALSE));
3527 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3528 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3529 NtUserReleaseDC( hwnd, hdc );
3530 return lppop->Height;
3534 /*******************************************************************
3535 * ChangeMenuA (USER32.@)
3537 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3538 UINT id, UINT flags )
3540 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3541 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3542 id, data );
3543 if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE );
3544 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3545 id, data );
3546 if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
3547 flags & MF_BYPOSITION ? pos : id,
3548 flags & ~MF_REMOVE );
3549 /* Default: MF_INSERT */
3550 return InsertMenuA( hMenu, pos, flags, id, data );
3554 /*******************************************************************
3555 * ChangeMenuW (USER32.@)
3557 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3558 UINT id, UINT flags )
3560 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3561 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3562 id, data );
3563 if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE );
3564 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3565 id, data );
3566 if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
3567 flags & MF_BYPOSITION ? pos : id,
3568 flags & ~MF_REMOVE );
3569 /* Default: MF_INSERT */
3570 return InsertMenuW( hMenu, pos, flags, id, data );
3574 /*******************************************************************
3575 * GetMenuStringA (USER32.@)
3577 INT WINAPI GetMenuStringA(
3578 HMENU hMenu, /* [in] menuhandle */
3579 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3580 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3581 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3582 UINT wFlags /* [in] MF_ flags */
3585 POPUPMENU *menu;
3586 MENUITEM *item;
3587 UINT pos;
3588 INT ret;
3590 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3591 if (str && nMaxSiz) str[0] = '\0';
3593 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3595 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3596 return 0;
3598 item = &menu->items[pos];
3600 if (!item->text)
3601 ret = 0;
3602 else if (!str || !nMaxSiz)
3603 ret = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3604 else
3606 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3607 str[nMaxSiz-1] = 0;
3608 ret = strlen(str);
3610 release_menu_ptr(menu);
3612 TRACE("returning %s\n", debugstr_a(str));
3613 return ret;
3617 /*******************************************************************
3618 * GetMenuStringW (USER32.@)
3620 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3621 LPWSTR str, INT nMaxSiz, UINT wFlags )
3623 POPUPMENU *menu;
3624 MENUITEM *item;
3625 UINT pos;
3626 INT ret;
3628 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3629 if (str && nMaxSiz) str[0] = '\0';
3631 if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos)))
3633 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3634 return 0;
3636 item = &menu->items[pos];
3638 if (!str || !nMaxSiz)
3639 ret = item->text ? lstrlenW(item->text) : 0;
3640 else if (!item->text)
3642 str[0] = 0;
3643 ret = 0;
3645 else
3647 lstrcpynW( str, item->text, nMaxSiz );
3648 ret = lstrlenW(str);
3650 release_menu_ptr(menu);
3652 TRACE("returning %s\n", debugstr_w(str));
3653 return ret;
3657 /**********************************************************************
3658 * HiliteMenuItem (USER32.@)
3660 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3661 UINT wHilite )
3663 POPUPMENU *menu;
3664 UINT pos;
3665 HMENU handle_menu;
3666 UINT focused_item;
3668 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3670 if (!(menu = find_menu_item(hMenu, wItemID, wHilite, &pos))) return FALSE;
3672 handle_menu = menu->obj.handle;
3673 focused_item = menu->FocusedItem;
3674 release_menu_ptr(menu);
3676 if (focused_item != pos)
3678 MENU_HideSubPopups( hWnd, handle_menu, FALSE, 0 );
3679 MENU_SelectItem( hWnd, handle_menu, pos, TRUE, 0 );
3682 return TRUE;
3686 /**********************************************************************
3687 * GetMenuState (USER32.@)
3689 UINT WINAPI GetMenuState( HMENU menu, UINT item, UINT flags )
3691 return NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuState, NULL, NULL );
3695 /**********************************************************************
3696 * GetMenuItemCount (USER32.@)
3698 INT WINAPI GetMenuItemCount( HMENU menu )
3700 return NtUserGetMenuItemCount( menu );
3704 /**********************************************************************
3705 * GetMenuItemID (USER32.@)
3707 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3709 POPUPMENU *menu;
3710 UINT id, pos;
3712 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
3713 return -1;
3715 id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID;
3716 release_menu_ptr(menu);
3717 return id;
3721 /**********************************************************************
3722 * MENU_mnu2mnuii
3724 * Uses flags, id and text ptr, passed by InsertMenu() and
3725 * ModifyMenu() to setup a MenuItemInfo structure.
3727 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3728 LPMENUITEMINFOW pmii)
3730 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3731 pmii->cbSize = sizeof( MENUITEMINFOW);
3732 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3733 /* setting bitmap clears text and vice versa */
3734 if( IS_STRING_ITEM(flags)) {
3735 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3736 if( !str)
3737 flags |= MF_SEPARATOR;
3738 /* Item beginning with a backspace is a help item */
3739 /* FIXME: wrong place, this is only true in win16 */
3740 else if( *str == '\b') {
3741 flags |= MF_HELP;
3742 str++;
3744 pmii->dwTypeData = (LPWSTR)str;
3745 } else if( flags & MFT_BITMAP){
3746 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3747 pmii->hbmpItem = (HBITMAP)str;
3749 if( flags & MF_OWNERDRAW){
3750 pmii->fMask |= MIIM_DATA;
3751 pmii->dwItemData = (ULONG_PTR) str;
3753 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3754 pmii->fMask |= MIIM_SUBMENU;
3755 pmii->hSubMenu = (HMENU)id;
3757 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3758 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3759 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3760 pmii->wID = (UINT)id;
3764 /*******************************************************************
3765 * InsertMenuW (USER32.@)
3767 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3768 UINT_PTR id, LPCWSTR str )
3770 MENUITEMINFOW mii;
3772 if (IS_STRING_ITEM(flags) && str)
3773 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3774 hMenu, pos, flags, id, debugstr_w(str) );
3775 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3776 hMenu, pos, flags, id, str );
3778 MENU_mnu2mnuii( flags, id, str, &mii);
3779 mii.fMask |= MIIM_CHECKMARKS;
3780 return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserInsertMenuItem, &mii, NULL );
3784 /*******************************************************************
3785 * InsertMenuA (USER32.@)
3787 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3788 UINT_PTR id, LPCSTR str )
3790 BOOL ret = FALSE;
3792 if (IS_STRING_ITEM(flags) && str)
3794 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3795 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3796 if (newstr)
3798 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3799 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3800 HeapFree( GetProcessHeap(), 0, newstr );
3802 return ret;
3804 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3808 /*******************************************************************
3809 * AppendMenuA (USER32.@)
3811 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3812 UINT_PTR id, LPCSTR data )
3814 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3818 /*******************************************************************
3819 * AppendMenuW (USER32.@)
3821 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3822 UINT_PTR id, LPCWSTR data )
3824 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3828 /*******************************************************************
3829 * ModifyMenuW (USER32.@)
3831 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3832 UINT_PTR id, LPCWSTR str )
3834 MENUITEMINFOW mii;
3836 if (IS_STRING_ITEM(flags))
3837 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3838 else
3839 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3841 MENU_mnu2mnuii( flags, id, str, &mii);
3842 return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserSetMenuItemInfo, &mii, NULL );
3846 /*******************************************************************
3847 * ModifyMenuA (USER32.@)
3849 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3850 UINT_PTR id, LPCSTR str )
3852 BOOL ret = FALSE;
3854 if (IS_STRING_ITEM(flags) && str)
3856 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3857 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3858 if (newstr)
3860 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3861 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3862 HeapFree( GetProcessHeap(), 0, newstr );
3864 return ret;
3866 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3870 /**********************************************************************
3871 * CreatePopupMenu (USER32.@)
3873 HMENU WINAPI CreatePopupMenu(void)
3875 return NtUserCreateMenu( TRUE );
3879 /**********************************************************************
3880 * GetMenuCheckMarkDimensions (USER.417)
3881 * GetMenuCheckMarkDimensions (USER32.@)
3883 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3885 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3889 /**********************************************************************
3890 * SetMenuItemBitmaps (USER32.@)
3892 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3893 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3895 POPUPMENU *menu;
3896 MENUITEM *item;
3897 UINT pos;
3899 if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos)))
3900 return FALSE;
3902 item = &menu->items[pos];
3903 if (!hNewCheck && !hNewUnCheck)
3905 item->fState &= ~MF_USECHECKBITMAPS;
3907 else /* Install new bitmaps */
3909 item->hCheckBit = hNewCheck;
3910 item->hUnCheckBit = hNewUnCheck;
3911 item->fState |= MF_USECHECKBITMAPS;
3913 release_menu_ptr(menu);
3915 return TRUE;
3919 /**********************************************************************
3920 * CreateMenu (USER32.@)
3922 HMENU WINAPI CreateMenu(void)
3924 return NtUserCreateMenu( FALSE );
3928 /**********************************************************************
3929 * GetMenu (USER32.@)
3931 HMENU WINAPI GetMenu( HWND hWnd )
3933 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3934 TRACE("for %p returning %p\n", hWnd, retvalue);
3935 return retvalue;
3938 /**********************************************************************
3939 * GetMenuBarInfo (USER32.@)
3941 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3943 POPUPMENU *menu;
3944 HMENU hmenu = NULL;
3945 ATOM class_atom;
3947 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
3949 switch (idObject)
3951 case OBJID_CLIENT:
3952 class_atom = GetClassLongW(hwnd, GCW_ATOM);
3953 if (!class_atom)
3954 return FALSE;
3955 if (class_atom != POPUPMENU_CLASS_ATOM)
3957 WARN("called on invalid window: %d\n", class_atom);
3958 SetLastError(ERROR_INVALID_MENU_HANDLE);
3959 return FALSE;
3962 hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
3963 break;
3964 case OBJID_MENU:
3965 hmenu = GetMenu(hwnd);
3966 break;
3967 case OBJID_SYSMENU:
3968 hmenu = NtUserGetSystemMenu( hwnd, FALSE );
3969 break;
3970 default:
3971 return FALSE;
3974 if (!hmenu)
3975 return FALSE;
3977 if (pmbi->cbSize != sizeof(MENUBARINFO))
3979 SetLastError(ERROR_INVALID_PARAMETER);
3980 return FALSE;
3983 menu = MENU_GetMenu(hmenu);
3984 if (!menu)
3985 return FALSE;
3986 if (idItem < 0 || idItem > menu->nItems)
3987 return FALSE;
3989 if (!menu->Height)
3991 SetRectEmpty(&pmbi->rcBar);
3993 else if (idItem == 0)
3995 NtUserGetMenuItemRect( hwnd, hmenu, 0, &pmbi->rcBar );
3996 pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
3997 pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
3999 else
4001 NtUserGetMenuItemRect( hwnd, hmenu, idItem - 1, &pmbi->rcBar );
4004 pmbi->hMenu = hmenu;
4005 pmbi->hwndMenu = NULL;
4006 pmbi->fBarFocused = top_popup_hmenu == hmenu;
4007 if (idItem)
4009 pmbi->fFocused = menu->FocusedItem == idItem - 1;
4010 if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
4012 menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
4013 if (menu)
4014 pmbi->hwndMenu = menu->hWnd;
4017 else
4019 pmbi->fFocused = pmbi->fBarFocused;
4022 return TRUE;
4026 /**********************************************************************
4027 * GetSubMenu (USER32.@)
4029 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4031 POPUPMENU *menu;
4032 HMENU submenu;
4033 UINT pos;
4035 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
4036 return 0;
4038 if (menu->items[pos].fType & MF_POPUP)
4039 submenu = menu->items[pos].hSubMenu;
4040 else
4041 submenu = 0;
4043 release_menu_ptr(menu);
4044 return submenu;
4048 /**********************************************************************
4049 * DrawMenuBar (USER32.@)
4051 BOOL WINAPI DrawMenuBar( HWND hwnd )
4053 return NtUserDrawMenuBar( hwnd );
4056 /***********************************************************************
4057 * DrawMenuBarTemp (USER32.@)
4059 * UNDOCUMENTED !!
4061 * called by W98SE desk.cpl Control Panel Applet
4063 * Not 100% sure about the param names, but close.
4065 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4067 LPPOPUPMENU lppop;
4068 UINT i,retvalue;
4069 HFONT hfontOld = 0;
4070 BOOL flat_menu = FALSE;
4072 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4074 if (!hMenu)
4075 hMenu = GetMenu(hwnd);
4077 if (!hFont)
4078 hFont = get_menu_font(FALSE);
4080 lppop = MENU_GetMenu( hMenu );
4081 if (lppop == NULL || lprect == NULL)
4083 retvalue = GetSystemMetrics(SM_CYMENU);
4084 goto END;
4087 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4089 hfontOld = SelectObject( hDC, hFont);
4091 if (lppop->Height == 0)
4092 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4094 lprect->bottom = lprect->top + lppop->Height;
4096 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4098 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4099 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4100 LineTo( hDC, lprect->right, lprect->bottom );
4102 if (lppop->nItems == 0)
4104 retvalue = GetSystemMetrics(SM_CYMENU);
4105 goto END;
4108 for (i = 0; i < lppop->nItems; i++)
4109 MENU_DrawMenuItem( hwnd, lppop, hwnd, hDC, &lppop->items[i], TRUE, ODA_DRAWENTIRE );
4111 retvalue = lppop->Height;
4113 END:
4114 if (hfontOld) SelectObject (hDC, hfontOld);
4115 return retvalue;
4118 /***********************************************************************
4119 * EndMenu (USER.187)
4120 * EndMenu (USER32.@)
4122 BOOL WINAPI EndMenu(void)
4124 /* if we are in the menu code, and it is active */
4125 if (!fEndMenu && top_popup)
4127 /* terminate the menu handling code */
4128 fEndMenu = TRUE;
4130 /* needs to be posted to wakeup the internal menu handler */
4131 /* which will now terminate the menu, in the event that */
4132 /* the main window was minimized, or lost focus, so we */
4133 /* don't end up with an orphaned menu */
4134 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4136 return fEndMenu;
4140 /*****************************************************************
4141 * LoadMenuA (USER32.@)
4143 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4145 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4146 if (!hrsrc) return 0;
4147 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4151 /*****************************************************************
4152 * LoadMenuW (USER32.@)
4154 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4156 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4157 if (!hrsrc) return 0;
4158 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4162 /**********************************************************************
4163 * LoadMenuIndirectW (USER32.@)
4165 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4167 HMENU hMenu;
4168 WORD version, offset;
4169 LPCSTR p = template;
4171 version = GET_WORD(p);
4172 p += sizeof(WORD);
4173 TRACE("%p, ver %d\n", template, version );
4174 switch (version)
4176 case 0: /* standard format is version of 0 */
4177 offset = GET_WORD(p);
4178 p += sizeof(WORD) + offset;
4179 if (!(hMenu = CreateMenu())) return 0;
4180 if (!MENU_ParseResource( p, hMenu ))
4182 NtUserDestroyMenu( hMenu );
4183 return 0;
4185 return hMenu;
4186 case 1: /* extended format is version of 1 */
4187 offset = GET_WORD(p);
4188 p += sizeof(WORD) + offset;
4189 if (!(hMenu = CreateMenu())) return 0;
4190 if (!MENUEX_ParseResource( p, hMenu))
4192 NtUserDestroyMenu( hMenu );
4193 return 0;
4195 return hMenu;
4196 default:
4197 ERR("version %d not supported.\n", version);
4198 return 0;
4203 /**********************************************************************
4204 * LoadMenuIndirectA (USER32.@)
4206 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4208 return LoadMenuIndirectW( template );
4212 /**********************************************************************
4213 * IsMenu (USER32.@)
4215 BOOL WINAPI IsMenu( HMENU menu )
4217 MENUINFO info;
4219 info.cbSize = sizeof(info);
4220 info.fMask = 0;
4221 if (GetMenuInfo( menu, &info )) return TRUE;
4223 SetLastError(ERROR_INVALID_MENU_HANDLE);
4224 return FALSE;
4227 /**********************************************************************
4228 * GetMenuItemInfo_common
4231 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT id, BOOL bypos,
4232 LPMENUITEMINFOW lpmii, BOOL unicode)
4234 POPUPMENU *menu;
4235 MENUITEM *item;
4236 UINT pos;
4238 menu = find_menu_item(hmenu, id, bypos ? MF_BYPOSITION : 0, &pos);
4240 item = menu ? &menu->items[pos] : NULL;
4242 debug_print_menuitem("GetMenuItemInfo_common: ", item, "");
4244 if (!menu)
4246 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4247 return FALSE;
4250 if( lpmii->fMask & MIIM_TYPE) {
4251 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4252 release_menu_ptr(menu);
4253 WARN("invalid combination of fMask bits used\n");
4254 /* this does not happen on Win9x/ME */
4255 SetLastError( ERROR_INVALID_PARAMETER);
4256 return FALSE;
4258 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4259 if (item->hbmpItem && !IS_MAGIC_BITMAP(item->hbmpItem))
4260 lpmii->fType |= MFT_BITMAP;
4261 lpmii->hbmpItem = item->hbmpItem; /* not on Win9x/ME */
4262 if( lpmii->fType & MFT_BITMAP) {
4263 lpmii->dwTypeData = (LPWSTR) item->hbmpItem;
4264 lpmii->cch = 0;
4265 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4266 /* this does not happen on Win9x/ME */
4267 lpmii->dwTypeData = 0;
4268 lpmii->cch = 0;
4272 /* copy the text string */
4273 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4274 if (!item->text) {
4275 if(lpmii->dwTypeData && lpmii->cch) {
4276 if( unicode)
4277 *((WCHAR *)lpmii->dwTypeData) = 0;
4278 else
4279 *((CHAR *)lpmii->dwTypeData) = 0;
4281 lpmii->cch = 0;
4282 } else {
4283 int len;
4284 if (unicode)
4286 len = lstrlenW(item->text);
4287 if(lpmii->dwTypeData && lpmii->cch)
4288 lstrcpynW(lpmii->dwTypeData, item->text, lpmii->cch);
4290 else
4292 len = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL,
4293 0, NULL, NULL ) - 1;
4294 if(lpmii->dwTypeData && lpmii->cch)
4295 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1,
4296 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4297 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4299 /* if we've copied a substring we return its length */
4300 if(lpmii->dwTypeData && lpmii->cch)
4301 if (lpmii->cch <= len + 1)
4302 lpmii->cch--;
4303 else
4304 lpmii->cch = len;
4305 else {
4306 /* return length of string */
4307 /* not on Win9x/ME if fType & MFT_BITMAP */
4308 lpmii->cch = len;
4313 if (lpmii->fMask & MIIM_FTYPE)
4314 lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK;
4316 if (lpmii->fMask & MIIM_BITMAP)
4317 lpmii->hbmpItem = item->hbmpItem;
4319 if (lpmii->fMask & MIIM_STATE)
4320 lpmii->fState = item->fState & MENUITEMINFO_STATE_MASK;
4322 if (lpmii->fMask & MIIM_ID)
4323 lpmii->wID = item->wID;
4325 if (lpmii->fMask & MIIM_SUBMENU)
4326 lpmii->hSubMenu = item->hSubMenu;
4327 else {
4328 /* hSubMenu is always cleared
4329 * (not on Win9x/ME ) */
4330 lpmii->hSubMenu = 0;
4333 if (lpmii->fMask & MIIM_CHECKMARKS) {
4334 lpmii->hbmpChecked = item->hCheckBit;
4335 lpmii->hbmpUnchecked = item->hUnCheckBit;
4337 if (lpmii->fMask & MIIM_DATA)
4338 lpmii->dwItemData = item->dwItemData;
4340 release_menu_ptr(menu);
4341 return TRUE;
4344 /**********************************************************************
4345 * GetMenuItemInfoA (USER32.@)
4347 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4348 LPMENUITEMINFOA lpmii)
4350 BOOL ret;
4351 MENUITEMINFOA mii;
4352 if( lpmii->cbSize != sizeof( mii) &&
4353 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4354 SetLastError( ERROR_INVALID_PARAMETER);
4355 return FALSE;
4357 memcpy( &mii, lpmii, lpmii->cbSize);
4358 mii.cbSize = sizeof( mii);
4359 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4360 (LPMENUITEMINFOW)&mii, FALSE);
4361 mii.cbSize = lpmii->cbSize;
4362 memcpy( lpmii, &mii, mii.cbSize);
4363 return ret;
4366 /**********************************************************************
4367 * GetMenuItemInfoW (USER32.@)
4369 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4370 LPMENUITEMINFOW lpmii)
4372 BOOL ret;
4373 MENUITEMINFOW mii;
4374 if( lpmii->cbSize != sizeof( mii) &&
4375 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4376 SetLastError( ERROR_INVALID_PARAMETER);
4377 return FALSE;
4379 memcpy( &mii, lpmii, lpmii->cbSize);
4380 mii.cbSize = sizeof( mii);
4381 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4382 mii.cbSize = lpmii->cbSize;
4383 memcpy( lpmii, &mii, mii.cbSize);
4384 return ret;
4388 /**********************************************************************
4389 * MENU_NormalizeMenuItemInfoStruct
4391 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4392 * check, copy and extend the MENUITEMINFO struct from the version that the application
4393 * supplied to the version used by wine source. */
4394 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4395 MENUITEMINFOW *pmii_out )
4397 /* do we recognize the size? */
4398 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4399 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4400 SetLastError( ERROR_INVALID_PARAMETER);
4401 return FALSE;
4403 /* copy the fields that we have */
4404 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4405 /* if the hbmpItem member is missing then extend */
4406 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4407 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4408 pmii_out->hbmpItem = NULL;
4410 /* test for invalid bit combinations */
4411 if( (pmii_out->fMask & MIIM_TYPE &&
4412 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4413 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4414 WARN("invalid combination of fMask bits used\n");
4415 /* this does not happen on Win9x/ME */
4416 SetLastError( ERROR_INVALID_PARAMETER);
4417 return FALSE;
4419 /* convert old style (MIIM_TYPE) to the new */
4420 if( pmii_out->fMask & MIIM_TYPE){
4421 pmii_out->fMask |= MIIM_FTYPE;
4422 if( IS_STRING_ITEM(pmii_out->fType)){
4423 pmii_out->fMask |= MIIM_STRING;
4424 } else if( (pmii_out->fType) & MFT_BITMAP){
4425 pmii_out->fMask |= MIIM_BITMAP;
4426 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4429 return TRUE;
4432 /**********************************************************************
4433 * SetMenuItemInfoA (USER32.@)
4435 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4436 const MENUITEMINFOA *lpmii)
4438 WCHAR *strW = NULL;
4439 MENUITEMINFOW mii;
4440 BOOL ret;
4442 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4444 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4446 if ((mii.fMask & MIIM_STRING) && mii.dwTypeData)
4448 const char *str = (const char *)mii.dwTypeData;
4449 UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4450 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
4451 MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len );
4452 mii.dwTypeData = strW;
4455 ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
4456 NtUserSetMenuItemInfo, &mii, NULL );
4458 HeapFree( GetProcessHeap(), 0, strW );
4459 return ret;
4462 /**********************************************************************
4463 * SetMenuItemInfoW (USER32.@)
4465 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4466 const MENUITEMINFOW *lpmii)
4468 MENUITEMINFOW mii;
4470 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4472 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4474 return NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
4475 NtUserSetMenuItemInfo, &mii, NULL );
4478 /**********************************************************************
4479 * GetMenuDefaultItem (USER32.@)
4481 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4483 POPUPMENU *menu;
4484 MENUITEM * item;
4485 UINT i = 0;
4487 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4489 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4491 /* find default item */
4492 item = menu->items;
4494 /* empty menu */
4495 if (! item) return -1;
4497 while ( !( item->fState & MFS_DEFAULT ) )
4499 i++; item++;
4500 if (i >= menu->nItems ) return -1;
4503 /* default: don't return disabled items */
4504 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4506 /* search rekursiv when needed */
4507 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4509 UINT ret;
4510 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4511 if ( -1 != ret ) return ret;
4513 /* when item not found in submenu, return the popup item */
4515 return ( bypos ) ? i : item->wID;
4520 /**********************************************************************
4521 * InsertMenuItemA (USER32.@)
4523 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4524 const MENUITEMINFOA *lpmii)
4526 WCHAR *strW = NULL;
4527 MENUITEMINFOW mii;
4528 BOOL ret;
4530 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4532 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4534 if ((mii.fMask & MIIM_STRING) && mii.dwTypeData)
4536 const char *str = (const char *)mii.dwTypeData;
4537 UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4538 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
4539 MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len );
4540 mii.dwTypeData = strW;
4543 ret = NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
4544 NtUserInsertMenuItem, &mii, NULL );
4546 HeapFree( GetProcessHeap(), 0, strW );
4547 return ret;
4551 /**********************************************************************
4552 * InsertMenuItemW (USER32.@)
4554 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4555 const MENUITEMINFOW *lpmii)
4557 MENUITEMINFOW mii;
4559 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4561 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4563 return NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
4564 NtUserInsertMenuItem, &mii, NULL );
4567 /**********************************************************************
4568 * CheckMenuRadioItem (USER32.@)
4571 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last,
4572 UINT check, UINT flags)
4574 POPUPMENU *first_menu = NULL, *check_menu;
4575 UINT i, check_pos;
4576 BOOL done = FALSE;
4578 for (i = first; i <= last; i++)
4580 MENUITEM *item;
4582 if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos)))
4583 continue;
4585 if (!first_menu)
4586 first_menu = grab_menu_ptr(check_menu->obj.handle);
4588 if (first_menu != check_menu)
4590 release_menu_ptr(check_menu);
4591 continue;
4594 item = &check_menu->items[check_pos];
4595 if (item->fType != MFT_SEPARATOR)
4597 if (i == check)
4599 item->fType |= MFT_RADIOCHECK;
4600 item->fState |= MFS_CHECKED;
4601 done = TRUE;
4603 else
4605 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4606 item->fState &= ~MFS_CHECKED;
4610 release_menu_ptr(check_menu);
4612 release_menu_ptr(first_menu);
4614 return done;
4618 /**********************************************************************
4619 * SetMenuInfo (USER32.@)
4621 BOOL WINAPI SetMenuInfo( HMENU menu, const MENUINFO *info )
4623 TRACE( "(%p %p)\n", menu, info );
4625 if (!info || info->cbSize != sizeof(*info))
4627 SetLastError( ERROR_INVALID_PARAMETER);
4628 return FALSE;
4631 return NtUserThunkedMenuInfo( menu, info );
4634 /**********************************************************************
4635 * GetMenuInfo (USER32.@)
4637 BOOL WINAPI GetMenuInfo( HMENU menu, MENUINFO *info )
4639 return NtUserGetMenuInfo( menu, info );
4643 /**********************************************************************
4644 * GetMenuContextHelpId (USER32.@)
4646 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4648 DWORD help_id = 0;
4649 POPUPMENU *menu;
4651 TRACE("(%p)\n", hMenu);
4653 if ((menu = grab_menu_ptr(hMenu)))
4655 help_id = menu->dwContextHelpID;
4656 release_menu_ptr(menu);
4659 return help_id;
4662 /**********************************************************************
4663 * MenuItemFromPoint (USER32.@)
4665 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4667 POPUPMENU *menu = grab_menu_ptr(hMenu);
4668 UINT pos;
4670 /*FIXME: Do we have to handle hWnd here? */
4671 if (!menu) return -1;
4673 if (MENU_FindItemByCoords( menu, ptScreen, &pos ) != ht_item)
4674 pos = -1;
4676 release_menu_ptr(menu);
4677 return pos;
4681 /**********************************************************************
4682 * CalcMenuBar (USER32.@)
4684 DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect)
4686 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd, left, right, top, rect);
4687 return 0;
4691 /**********************************************************************
4692 * TranslateAcceleratorA (USER32.@)
4693 * TranslateAccelerator (USER32.@)
4695 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4697 switch (msg->message)
4699 case WM_KEYDOWN:
4700 case WM_SYSKEYDOWN:
4701 return NtUserTranslateAccelerator( hWnd, hAccel, msg );
4703 case WM_CHAR:
4704 case WM_SYSCHAR:
4706 MSG msgW = *msg;
4707 char ch = LOWORD(msg->wParam);
4708 WCHAR wch;
4709 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4710 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
4711 return NtUserTranslateAccelerator( hWnd, hAccel, &msgW );
4714 default:
4715 return 0;