server: Fix connect failures on newer kernels.
[wine.git] / dlls / user32 / menu.c
blob4d2edf8ab1cbc36ed0d94fd3805098a953458ae4
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class =
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 const char *postfix)
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
229 if (mp) {
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
232 if ( mp->hSubMenu)
233 TRACE( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 TRACE( ", fType=");
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 if (flags)
248 TRACE( "+0x%x", flags);
250 flags = mp->fState;
251 if (flags) {
252 int count = 0;
253 TRACE( ", State=");
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
261 if (flags)
262 TRACE( "+0x%x", flags);
264 if (mp->hCheckBit)
265 TRACE( ", Chk=%p", mp->hCheckBit);
266 if (mp->hUnCheckBit)
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 if (mp->text)
269 TRACE( ", Text=%s", debugstr_w(mp->text));
270 if (mp->dwItemData)
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if (mp->hbmpItem)
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 else
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
279 TRACE( " }");
280 } else
281 TRACE( "NULL");
282 TRACE(" %s\n", postfix);
285 #undef MENUOUT
286 #undef MENUFLAG
289 /***********************************************************************
290 * MENU_GetMenu
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
298 if (menu == OBJ_OTHER_PROCESS)
300 WARN( "other process menu %p?\n", hMenu);
301 return NULL;
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
305 return menu;
308 /***********************************************************************
309 * get_win_sys_menu
311 * Get the system menu of a window
313 static HMENU get_win_sys_menu( HWND hwnd )
315 HMENU ret = 0;
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
319 ret = win->hSysMenu;
320 WIN_ReleasePtr( win );
322 return ret;
325 /***********************************************************************
326 * get_menu_font
328 static HFONT get_menu_font( BOOL bold )
330 static HFONT hMenuFont, hMenuFontBold;
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
334 if (!ret)
336 NONCLIENTMETRICSW ncm;
337 HFONT prev;
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
342 if (bold)
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 ret, NULL );
350 if (prev)
352 /* another thread beat us to it */
353 DeleteObject( ret );
354 ret = prev;
357 return ret;
360 /***********************************************************************
361 * get_arrow_bitmap
363 static HBITMAP get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap;
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 return arrow_bitmap;
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap;
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
379 return arrow_bitmap;
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap;
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
390 return arrow_bitmap;
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap;
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
401 return arrow_bitmap;
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap;
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
412 return arrow_bitmap;
415 /***********************************************************************
416 * MENU_CopySysPopup
418 * Return the default system menu.
420 static HMENU MENU_CopySysPopup(void)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
425 if( hMenu ) {
426 MENUINFO minfo;
427 MENUITEMINFOW miteminfo;
428 POPUPMENU* menu = MENU_GetMenu(hMenu);
429 menu->wFlags |= MF_SYSMENU | MF_POPUP;
430 /* decorate the menu with bitmaps */
431 minfo.cbSize = sizeof( MENUINFO);
432 minfo.dwStyle = MNS_CHECKORBMP;
433 minfo.fMask = MIM_STYLE;
434 SetMenuInfo( hMenu, &minfo);
435 miteminfo.cbSize = sizeof( MENUITEMINFOW);
436 miteminfo.fMask = MIIM_BITMAP;
437 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
438 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
439 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
440 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
441 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
442 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
443 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
444 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
445 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
447 else
448 ERR("Unable to load default system menu\n" );
450 TRACE("returning %p.\n", hMenu );
452 return hMenu;
456 /**********************************************************************
457 * MENU_GetSysMenu
459 * Create a copy of the system menu. System menu in Windows is
460 * a special menu bar with the single entry - system menu popup.
461 * This popup is presented to the outside world as a "system menu".
462 * However, the real system menu handle is sometimes seen in the
463 * WM_MENUSELECT parameters (and Word 6 likes it this way).
465 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
467 HMENU hMenu;
469 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
470 if ((hMenu = CreateMenu()))
472 POPUPMENU *menu = MENU_GetMenu(hMenu);
473 menu->wFlags = MF_SYSMENU;
474 menu->hWnd = WIN_GetFullHandle( hWnd );
475 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
477 if (!hPopupMenu)
478 hPopupMenu = MENU_CopySysPopup();
480 if (hPopupMenu)
482 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
483 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
485 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
486 (UINT_PTR)hPopupMenu, NULL );
488 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
489 menu->items[0].fState = 0;
490 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
492 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
493 return hMenu;
495 DestroyMenu( hMenu );
497 ERR("failed to load system menu!\n");
498 return 0;
502 /***********************************************************************
503 * MENU_InitSysMenuPopup
505 * Grey the appropriate items in System menu.
507 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
509 BOOL gray;
511 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
512 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
513 gray = ((style & WS_MAXIMIZE) != 0);
514 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
515 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
516 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
517 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
518 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
520 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = (clsStyle & CS_NOCLOSE) != 0;
523 /* The menu item must keep its state if it's disabled */
524 if(gray)
525 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
529 /******************************************************************************
531 * UINT MENU_GetStartOfNextColumn(
532 * HMENU hMenu )
534 *****************************************************************************/
536 static UINT MENU_GetStartOfNextColumn(
537 HMENU hMenu )
539 POPUPMENU *menu = MENU_GetMenu(hMenu);
540 UINT i;
542 if(!menu)
543 return NO_SELECTED_ITEM;
545 i = menu->FocusedItem + 1;
546 if( i == NO_SELECTED_ITEM )
547 return i;
549 for( ; i < menu->nItems; ++i ) {
550 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
551 return i;
554 return NO_SELECTED_ITEM;
558 /******************************************************************************
560 * UINT MENU_GetStartOfPrevColumn(
561 * HMENU hMenu )
563 *****************************************************************************/
565 static UINT MENU_GetStartOfPrevColumn(
566 HMENU hMenu )
568 POPUPMENU *menu = MENU_GetMenu(hMenu);
569 UINT i;
571 if( !menu )
572 return NO_SELECTED_ITEM;
574 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
575 return NO_SELECTED_ITEM;
577 /* Find the start of the column */
579 for(i = menu->FocusedItem; i != 0 &&
580 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
581 --i); /* empty */
583 if(i == 0)
584 return NO_SELECTED_ITEM;
586 for(--i; i != 0; --i) {
587 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
588 break;
591 TRACE("ret %d.\n", i );
593 return i;
598 /***********************************************************************
599 * MENU_FindItem
601 * Find a menu item. Return a pointer on the item, and modifies *hmenu
602 * in case the item was in a sub-menu.
604 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
606 POPUPMENU *menu;
607 MENUITEM *fallback = NULL;
608 UINT fallback_pos = 0;
609 UINT i;
611 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
612 if (wFlags & MF_BYPOSITION)
614 if (*nPos >= menu->nItems) return NULL;
615 return &menu->items[*nPos];
617 else
619 MENUITEM *item = menu->items;
620 for (i = 0; i < menu->nItems; i++, item++)
622 if (item->fType & MF_POPUP)
624 HMENU hsubmenu = item->hSubMenu;
625 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
626 if (subitem)
628 *hmenu = hsubmenu;
629 return subitem;
631 else if (item->wID == *nPos)
633 /* fallback to this item if nothing else found */
634 fallback_pos = i;
635 fallback = item;
638 else if (item->wID == *nPos)
640 *nPos = i;
641 return item;
646 if (fallback)
647 *nPos = fallback_pos;
649 return fallback;
652 /***********************************************************************
653 * MENU_FindSubMenu
655 * Find a Sub menu. Return the position of the submenu, and modifies
656 * *hmenu in case it is found in another sub-menu.
657 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
659 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
661 POPUPMENU *menu;
662 UINT i;
663 MENUITEM *item;
664 if (((*hmenu)==(HMENU)0xffff) ||
665 (!(menu = MENU_GetMenu(*hmenu))))
666 return NO_SELECTED_ITEM;
667 item = menu->items;
668 for (i = 0; i < menu->nItems; i++, item++) {
669 if(!(item->fType & MF_POPUP)) continue;
670 if (item->hSubMenu == hSubTarget) {
671 return i;
673 else {
674 HMENU hsubmenu = item->hSubMenu;
675 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
676 if (pos != NO_SELECTED_ITEM) {
677 *hmenu = hsubmenu;
678 return pos;
682 return NO_SELECTED_ITEM;
685 /***********************************************************************
686 * MENU_FreeItemData
688 static void MENU_FreeItemData( MENUITEM* item )
690 /* delete text */
691 HeapFree( GetProcessHeap(), 0, item->text );
694 /***********************************************************************
695 * MENU_AdjustMenuItemRect
697 * Adjust menu item rectangle according to scrolling state.
699 static void
700 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
702 if (menu->bScrolling)
704 UINT arrow_bitmap_height;
705 BITMAP bmp;
707 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
708 arrow_bitmap_height = bmp.bmHeight;
709 rect->top += arrow_bitmap_height - menu->nScrollPos;
710 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
715 /***********************************************************************
716 * MENU_FindItemByCoords
718 * Find the item at the specified coordinates (screen coords). Does
719 * not work for child windows and therefore should not be called for
720 * an arbitrary system menu.
722 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
723 POINT pt, UINT *pos )
725 MENUITEM *item;
726 UINT i;
727 RECT rect;
729 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
730 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
731 else pt.x -= rect.left;
732 pt.y -= rect.top;
733 item = menu->items;
734 for (i = 0; i < menu->nItems; i++, item++)
736 rect = item->rect;
737 MENU_AdjustMenuItemRect(menu, &rect);
738 if (PtInRect(&rect, pt))
740 if (pos) *pos = i;
741 return item;
744 return NULL;
748 /***********************************************************************
749 * MENU_FindItemByKey
751 * Find the menu item selected by a key press.
752 * Return item id, -1 if none, -2 if we should close the menu.
754 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
755 WCHAR key, BOOL forceMenuChar )
757 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
759 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
761 if (hmenu)
763 POPUPMENU *menu = MENU_GetMenu( hmenu );
764 MENUITEM *item = menu->items;
765 LRESULT menuchar;
767 if( !forceMenuChar )
769 UINT i;
771 for (i = 0; i < menu->nItems; i++, item++)
773 if( item->text)
775 WCHAR *p = item->text - 2;
778 p = strchrW (p + 2, '&');
780 while (p != NULL && p [1] == '&');
781 if (p && (toupperW(p[1]) == toupperW(key))) return i;
785 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
786 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
787 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
788 if (HIWORD(menuchar) == 1) return (UINT)(-2);
790 return (UINT)(-1);
794 /***********************************************************************
795 * MENU_GetBitmapItemSize
797 * Get the size of a bitmap item.
799 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
800 HWND hwndOwner)
802 BITMAP bm;
803 HBITMAP bmp = lpitem->hbmpItem;
805 size->cx = size->cy = 0;
807 /* check if there is a magic menu item associated with this item */
808 switch( (INT_PTR) bmp )
810 case (INT_PTR)HBMMENU_CALLBACK:
812 MEASUREITEMSTRUCT measItem;
813 measItem.CtlType = ODT_MENU;
814 measItem.CtlID = 0;
815 measItem.itemID = lpitem->wID;
816 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
817 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
818 measItem.itemData = lpitem->dwItemData;
819 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
820 size->cx = measItem.itemWidth;
821 size->cy = measItem.itemHeight;
822 return;
824 break;
825 case (INT_PTR)HBMMENU_SYSTEM:
826 if (lpitem->dwItemData)
828 bmp = (HBITMAP)lpitem->dwItemData;
829 break;
831 /* fall through */
832 case (INT_PTR)HBMMENU_MBAR_RESTORE:
833 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
834 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
835 case (INT_PTR)HBMMENU_MBAR_CLOSE:
836 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
837 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
838 size->cy = size->cx;
839 return;
840 case (INT_PTR)HBMMENU_POPUP_CLOSE:
841 case (INT_PTR)HBMMENU_POPUP_RESTORE:
842 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
843 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
844 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
845 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
846 return;
848 if (GetObjectW(bmp, sizeof(bm), &bm ))
850 size->cx = bm.bmWidth;
851 size->cy = bm.bmHeight;
855 /***********************************************************************
856 * MENU_DrawBitmapItem
858 * Draw a bitmap item.
860 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
861 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
863 BITMAP bm;
864 DWORD rop;
865 HDC hdcMem;
866 HBITMAP bmp;
867 int w = rect->right - rect->left;
868 int h = rect->bottom - rect->top;
869 int bmp_xoffset = 0;
870 int left, top;
871 HBITMAP hbmToDraw = lpitem->hbmpItem;
872 bmp = hbmToDraw;
874 /* Check if there is a magic menu item associated with this item */
875 if (IS_MAGIC_BITMAP(hbmToDraw))
877 UINT flags = 0;
878 WCHAR bmchr = 0;
879 RECT r;
881 switch((INT_PTR)hbmToDraw)
883 case (INT_PTR)HBMMENU_SYSTEM:
884 if (lpitem->dwItemData)
886 bmp = (HBITMAP)lpitem->dwItemData;
887 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
889 else
891 static HBITMAP hBmpSysMenu;
893 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
894 bmp = hBmpSysMenu;
895 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
896 /* only use right half of the bitmap */
897 bmp_xoffset = bm.bmWidth / 2;
898 bm.bmWidth -= bmp_xoffset;
900 goto got_bitmap;
901 case (INT_PTR)HBMMENU_MBAR_RESTORE:
902 flags = DFCS_CAPTIONRESTORE;
903 break;
904 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
905 flags = DFCS_CAPTIONMIN;
906 break;
907 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
908 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
909 break;
910 case (INT_PTR)HBMMENU_MBAR_CLOSE:
911 flags = DFCS_CAPTIONCLOSE;
912 break;
913 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
914 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
915 break;
916 case (INT_PTR)HBMMENU_CALLBACK:
918 DRAWITEMSTRUCT drawItem;
919 drawItem.CtlType = ODT_MENU;
920 drawItem.CtlID = 0;
921 drawItem.itemID = lpitem->wID;
922 drawItem.itemAction = odaction;
923 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
924 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
925 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
926 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
927 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
928 drawItem.hwndItem = (HWND)hmenu;
929 drawItem.hDC = hdc;
930 drawItem.itemData = lpitem->dwItemData;
931 drawItem.rcItem = *rect;
932 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
933 return;
935 break;
936 case (INT_PTR)HBMMENU_POPUP_CLOSE:
937 bmchr = 0x72;
938 break;
939 case (INT_PTR)HBMMENU_POPUP_RESTORE:
940 bmchr = 0x32;
941 break;
942 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
943 bmchr = 0x31;
944 break;
945 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
946 bmchr = 0x30;
947 break;
948 default:
949 FIXME("Magic %p not implemented\n", hbmToDraw);
950 return;
952 if (bmchr)
954 /* draw the magic bitmaps using marlett font characters */
955 /* FIXME: fontsize and the position (x,y) could probably be better */
956 HFONT hfont, hfontsav;
957 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
958 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
959 { 'M','a','r','l','e','t','t',0 } };
960 logfont.lfHeight = min( h, w) - 5 ;
961 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
962 hfont = CreateFontIndirectW( &logfont);
963 hfontsav = SelectObject(hdc, hfont);
964 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
965 SelectObject(hdc, hfontsav);
966 DeleteObject( hfont);
968 else
970 r = *rect;
971 InflateRect( &r, -1, -1 );
972 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
973 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
975 return;
978 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
980 got_bitmap:
981 hdcMem = CreateCompatibleDC( hdc );
982 SelectObject( hdcMem, bmp );
984 /* handle fontsize > bitmap_height */
985 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
986 left=rect->left;
987 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
988 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
989 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
990 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
991 DeleteDC( hdcMem );
995 /***********************************************************************
996 * MENU_CalcItemSize
998 * Calculate the size of the menu item and store it in lpitem->rect.
1000 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1001 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1003 WCHAR *p;
1004 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1005 UINT arrow_bitmap_width;
1006 BITMAP bm;
1007 INT itemheight;
1009 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1010 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1011 (menuBar ? " (MenuBar)" : ""));
1013 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1014 arrow_bitmap_width = bm.bmWidth;
1016 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1017 if( !menucharsize.cx ) {
1018 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1019 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1020 * but it is unlikely an application will depend on that */
1021 ODitemheight = HIWORD( GetDialogBaseUnits());
1024 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1026 if (lpitem->fType & MF_OWNERDRAW)
1028 MEASUREITEMSTRUCT mis;
1029 mis.CtlType = ODT_MENU;
1030 mis.CtlID = 0;
1031 mis.itemID = lpitem->wID;
1032 mis.itemData = lpitem->dwItemData;
1033 mis.itemHeight = ODitemheight;
1034 mis.itemWidth = 0;
1035 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1036 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1037 * width of a menufont character to the width of an owner-drawn menu.
1039 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1040 if (menuBar) {
1041 /* under at least win95 you seem to be given a standard
1042 height for the menu and the height value is ignored */
1043 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1044 } else
1045 lpitem->rect.bottom += mis.itemHeight;
1047 TRACE("id=%04lx size=%dx%d\n",
1048 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1049 lpitem->rect.bottom-lpitem->rect.top);
1050 return;
1053 if (lpitem->fType & MF_SEPARATOR)
1055 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1056 if( !menuBar)
1057 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1058 return;
1061 itemheight = 0;
1062 lpitem->xTab = 0;
1064 if (!menuBar) {
1065 if (lpitem->hbmpItem) {
1066 SIZE size;
1068 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1069 /* Keep the size of the bitmap in callback mode to be able
1070 * to draw it correctly */
1071 lpitem->bmpsize = size;
1072 lppop->textOffset = max( lppop->textOffset, size.cx);
1073 lpitem->rect.right += size.cx + 2;
1074 itemheight = size.cy + 2;
1076 if( !(lppop->dwStyle & MNS_NOCHECK))
1077 lpitem->rect.right += check_bitmap_width;
1078 lpitem->rect.right += 4 + menucharsize.cx;
1079 lpitem->xTab = lpitem->rect.right;
1080 lpitem->rect.right += arrow_bitmap_width;
1081 } else if (lpitem->hbmpItem) { /* menuBar */
1082 SIZE size;
1084 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1085 lpitem->bmpsize = size;
1086 lpitem->rect.right += size.cx;
1087 if( lpitem->text) lpitem->rect.right += 2;
1088 itemheight = size.cy;
1091 /* it must be a text item - unless it's the system menu */
1092 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1093 HFONT hfontOld = NULL;
1094 RECT rc = lpitem->rect;
1095 LONG txtheight, txtwidth;
1097 if ( lpitem->fState & MFS_DEFAULT ) {
1098 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1100 if (menuBar) {
1101 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1102 DT_SINGLELINE|DT_CALCRECT);
1103 lpitem->rect.right += rc.right - rc.left;
1104 itemheight = max( max( itemheight, txtheight),
1105 GetSystemMetrics( SM_CYMENU) - 1);
1106 lpitem->rect.right += 2 * menucharsize.cx;
1107 } else {
1108 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1109 RECT tmprc = rc;
1110 LONG tmpheight;
1111 int n = (int)( p - lpitem->text);
1112 /* Item contains a tab (only meaningful in popup menus) */
1113 /* get text size before the tab */
1114 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1115 DT_SINGLELINE|DT_CALCRECT);
1116 txtwidth = rc.right - rc.left;
1117 p += 1; /* advance past the Tab */
1118 /* get text size after the tab */
1119 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1120 DT_SINGLELINE|DT_CALCRECT);
1121 lpitem->xTab += txtwidth;
1122 txtheight = max( txtheight, tmpheight);
1123 txtwidth += menucharsize.cx + /* space for the tab */
1124 tmprc.right - tmprc.left; /* space for the short cut */
1125 } else {
1126 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1127 DT_SINGLELINE|DT_CALCRECT);
1128 txtwidth = rc.right - rc.left;
1129 lpitem->xTab += txtwidth;
1131 lpitem->rect.right += 2 + txtwidth;
1132 itemheight = max( itemheight,
1133 max( txtheight + 2, menucharsize.cy + 4));
1135 if (hfontOld) SelectObject (hdc, hfontOld);
1136 } else if( menuBar) {
1137 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1139 lpitem->rect.bottom += itemheight;
1140 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1144 /***********************************************************************
1145 * MENU_GetMaxPopupHeight
1147 static UINT
1148 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1150 if (lppop->cyMax)
1151 return lppop->cyMax;
1152 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1156 /***********************************************************************
1157 * MENU_PopupMenuCalcSize
1159 * Calculate the size of a popup menu.
1161 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1163 MENUITEM *lpitem;
1164 HDC hdc;
1165 UINT start, i;
1166 int textandbmp = FALSE;
1167 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1169 lppop->Width = lppop->Height = 0;
1170 if (lppop->nItems == 0) return;
1171 hdc = GetDC( 0 );
1173 SelectObject( hdc, get_menu_font(FALSE));
1175 start = 0;
1176 maxX = 2 + 1;
1178 lppop->textOffset = 0;
1180 while (start < lppop->nItems)
1182 lpitem = &lppop->items[start];
1183 orgX = maxX;
1184 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1185 orgX += MENU_COL_SPACE;
1186 orgY = MENU_TOP_MARGIN;
1188 maxTab = maxTabWidth = 0;
1189 /* Parse items until column break or end of menu */
1190 for (i = start; i < lppop->nItems; i++, lpitem++)
1192 if ((i != start) &&
1193 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1195 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1196 maxX = max( maxX, lpitem->rect.right );
1197 orgY = lpitem->rect.bottom;
1198 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1200 maxTab = max( maxTab, lpitem->xTab );
1201 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1203 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1206 /* Finish the column (set all items to the largest width found) */
1207 maxX = max( maxX, maxTab + maxTabWidth );
1208 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1210 lpitem->rect.right = maxX;
1211 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1212 lpitem->xTab = maxTab;
1215 lppop->Height = max( lppop->Height, orgY );
1218 lppop->Width = maxX;
1219 /* if none of the items have both text and bitmap then
1220 * the text and bitmaps are all aligned on the left. If there is at
1221 * least one item with both text and bitmap then bitmaps are
1222 * on the left and texts left aligned with the right hand side
1223 * of the bitmaps */
1224 if( !textandbmp) lppop->textOffset = 0;
1226 /* space for 3d border */
1227 lppop->Height += MENU_BOTTOM_MARGIN;
1228 lppop->Width += 2;
1230 /* Adjust popup height if it exceeds maximum */
1231 maxHeight = MENU_GetMaxPopupHeight(lppop);
1232 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1233 if (lppop->Height >= maxHeight)
1235 lppop->Height = maxHeight;
1236 lppop->bScrolling = TRUE;
1238 else
1240 lppop->bScrolling = FALSE;
1243 ReleaseDC( 0, hdc );
1247 /***********************************************************************
1248 * MENU_MenuBarCalcSize
1250 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1251 * height is off by 1 pixel which causes lengthy window relocations when
1252 * active document window is maximized/restored.
1254 * Calculate the size of the menu bar.
1256 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1257 LPPOPUPMENU lppop, HWND hwndOwner )
1259 MENUITEM *lpitem;
1260 UINT start, i, helpPos;
1261 int orgX, orgY, maxY;
1263 if ((lprect == NULL) || (lppop == NULL)) return;
1264 if (lppop->nItems == 0) return;
1265 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1266 lppop->Width = lprect->right - lprect->left;
1267 lppop->Height = 0;
1268 maxY = lprect->top+1;
1269 start = 0;
1270 helpPos = ~0U;
1271 lppop->textOffset = 0;
1272 while (start < lppop->nItems)
1274 lpitem = &lppop->items[start];
1275 orgX = lprect->left;
1276 orgY = maxY;
1278 /* Parse items until line break or end of menu */
1279 for (i = start; i < lppop->nItems; i++, lpitem++)
1281 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1282 if ((i != start) &&
1283 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1285 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1286 debug_print_menuitem (" item: ", lpitem, "");
1287 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1289 if (lpitem->rect.right > lprect->right)
1291 if (i != start) break;
1292 else lpitem->rect.right = lprect->right;
1294 maxY = max( maxY, lpitem->rect.bottom );
1295 orgX = lpitem->rect.right;
1298 /* Finish the line (set all items to the largest height found) */
1299 while (start < i) lppop->items[start++].rect.bottom = maxY;
1302 lprect->bottom = maxY;
1303 lppop->Height = lprect->bottom - lprect->top;
1305 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1306 /* the last item (if several lines, only move the last line) */
1307 if (helpPos == ~0U) return;
1308 lpitem = &lppop->items[lppop->nItems-1];
1309 orgY = lpitem->rect.top;
1310 orgX = lprect->right;
1311 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1312 if (lpitem->rect.top != orgY) break; /* Other line */
1313 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1314 lpitem->rect.left += orgX - lpitem->rect.right;
1315 lpitem->rect.right = orgX;
1316 orgX = lpitem->rect.left;
1321 /***********************************************************************
1322 * MENU_DrawScrollArrows
1324 * Draw scroll arrows.
1326 static void
1327 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1329 HDC hdcMem = CreateCompatibleDC(hdc);
1330 HBITMAP hOrigBitmap;
1331 UINT arrow_bitmap_width, arrow_bitmap_height;
1332 BITMAP bmp;
1333 RECT rect;
1335 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1336 arrow_bitmap_width = bmp.bmWidth;
1337 arrow_bitmap_height = bmp.bmHeight;
1340 if (lppop->nScrollPos)
1341 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1342 else
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1344 rect.left = 0;
1345 rect.top = 0;
1346 rect.right = lppop->Width;
1347 rect.bottom = arrow_bitmap_height;
1348 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1349 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1350 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1351 rect.top = lppop->Height - arrow_bitmap_height;
1352 rect.bottom = lppop->Height;
1353 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1354 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1355 SelectObject(hdcMem, get_down_arrow_bitmap());
1356 else
1357 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1358 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1359 lppop->Height - arrow_bitmap_height,
1360 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1361 SelectObject(hdcMem, hOrigBitmap);
1362 DeleteDC(hdcMem);
1366 /***********************************************************************
1367 * draw_popup_arrow
1369 * Draws the popup-menu arrow.
1371 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1372 UINT arrow_bitmap_height)
1374 HDC hdcMem = CreateCompatibleDC( hdc );
1375 HBITMAP hOrigBitmap;
1377 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1378 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1379 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1380 arrow_bitmap_width, arrow_bitmap_height,
1381 hdcMem, 0, 0, SRCCOPY );
1382 SelectObject( hdcMem, hOrigBitmap );
1383 DeleteDC( hdcMem );
1385 /***********************************************************************
1386 * MENU_DrawMenuItem
1388 * Draw a single menu item.
1390 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1391 UINT height, BOOL menuBar, UINT odaction )
1393 RECT rect;
1394 BOOL flat_menu = FALSE;
1395 int bkgnd;
1396 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1397 POPUPMENU *menu = MENU_GetMenu(hmenu);
1398 RECT bmprc;
1400 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1402 if (!menuBar) {
1403 BITMAP bmp;
1404 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1405 arrow_bitmap_width = bmp.bmWidth;
1406 arrow_bitmap_height = bmp.bmHeight;
1409 if (lpitem->fType & MF_SYSMENU)
1411 if( !IsIconic(hwnd) )
1412 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1413 return;
1416 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1417 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1419 /* Setup colors */
1421 if (lpitem->fState & MF_HILITE)
1423 if(menuBar && !flat_menu) {
1424 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1425 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1426 } else {
1427 if(lpitem->fState & MF_GRAYED)
1428 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1429 else
1430 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1431 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1434 else
1436 if (lpitem->fState & MF_GRAYED)
1437 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1438 else
1439 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1440 SetBkColor( hdc, GetSysColor( bkgnd ) );
1443 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1444 rect = lpitem->rect;
1445 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1447 if (lpitem->fType & MF_OWNERDRAW)
1450 ** Experimentation under Windows reveals that an owner-drawn
1451 ** menu is given the rectangle which includes the space it requested
1452 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1453 ** and a popup-menu arrow. This is the value of lpitem->rect.
1454 ** Windows will leave all drawing to the application except for
1455 ** the popup-menu arrow. Windows always draws that itself, after
1456 ** the menu owner has finished drawing.
1458 DRAWITEMSTRUCT dis;
1460 dis.CtlType = ODT_MENU;
1461 dis.CtlID = 0;
1462 dis.itemID = lpitem->wID;
1463 dis.itemData = lpitem->dwItemData;
1464 dis.itemState = 0;
1465 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1466 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1467 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1468 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1469 dis.hwndItem = (HWND)hmenu;
1470 dis.hDC = hdc;
1471 dis.rcItem = rect;
1472 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1473 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1474 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1475 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1476 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1477 /* Draw the popup-menu arrow */
1478 if (lpitem->fType & MF_POPUP)
1479 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1480 arrow_bitmap_height);
1481 return;
1484 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1486 if (lpitem->fState & MF_HILITE)
1488 if (flat_menu)
1490 InflateRect (&rect, -1, -1);
1491 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1492 InflateRect (&rect, 1, 1);
1493 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1495 else
1497 if(menuBar)
1498 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1499 else
1500 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1503 else
1504 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1506 SetBkMode( hdc, TRANSPARENT );
1508 /* vertical separator */
1509 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1511 HPEN oldPen;
1512 RECT rc = rect;
1514 rc.left -= MENU_COL_SPACE / 2 + 1;
1515 rc.top = 3;
1516 rc.bottom = height - 3;
1517 if (flat_menu)
1519 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1520 MoveToEx( hdc, rc.left, rc.top, NULL );
1521 LineTo( hdc, rc.left, rc.bottom );
1522 SelectObject( hdc, oldPen );
1524 else
1525 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1528 /* horizontal separator */
1529 if (lpitem->fType & MF_SEPARATOR)
1531 HPEN oldPen;
1532 RECT rc = rect;
1534 rc.left++;
1535 rc.right--;
1536 rc.top = ( rc.top + rc.bottom) / 2;
1537 if (flat_menu)
1539 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1540 MoveToEx( hdc, rc.left, rc.top, NULL );
1541 LineTo( hdc, rc.right, rc.top );
1542 SelectObject( hdc, oldPen );
1544 else
1545 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1546 return;
1549 /* helper lines for debugging */
1550 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1551 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1552 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1553 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1556 if (lpitem->hbmpItem) {
1557 /* calculate the bitmap rectangle in coordinates relative
1558 * to the item rectangle */
1559 if( menuBar) {
1560 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1561 bmprc.left = 3;
1562 else
1563 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1565 else if (menu->dwStyle & MNS_NOCHECK)
1566 bmprc.left = 4;
1567 else if (menu->dwStyle & MNS_CHECKORBMP)
1568 bmprc.left = 2;
1569 else
1570 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1571 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1572 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1573 bmprc.top = 0;
1574 else
1575 bmprc.top = (rect.bottom - rect.top -
1576 lpitem->bmpsize.cy) / 2;
1577 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1580 if (!menuBar)
1582 HBITMAP bm;
1583 INT y = rect.top + rect.bottom;
1584 RECT rc = rect;
1585 int checked = FALSE;
1586 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1587 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1588 /* Draw the check mark
1590 * FIXME:
1591 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1593 if( !(menu->dwStyle & MNS_NOCHECK)) {
1594 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1595 lpitem->hUnCheckBit;
1596 if (bm) /* we have a custom bitmap */
1598 HDC hdcMem = CreateCompatibleDC( hdc );
1600 SelectObject( hdcMem, bm );
1601 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1602 check_bitmap_width, check_bitmap_height,
1603 hdcMem, 0, 0, SRCCOPY );
1604 DeleteDC( hdcMem );
1605 checked = TRUE;
1607 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1609 RECT r;
1610 HBITMAP bm = CreateBitmap( check_bitmap_width,
1611 check_bitmap_height, 1, 1, NULL );
1612 HDC hdcMem = CreateCompatibleDC( hdc );
1614 SelectObject( hdcMem, bm );
1615 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1616 DrawFrameControl( hdcMem, &r, DFC_MENU,
1617 (lpitem->fType & MFT_RADIOCHECK) ?
1618 DFCS_MENUBULLET : DFCS_MENUCHECK );
1619 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1620 hdcMem, 0, 0, SRCCOPY );
1621 DeleteDC( hdcMem );
1622 DeleteObject( bm );
1623 checked = TRUE;
1626 if( lpitem->hbmpItem &&
1627 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1628 POINT origorg;
1629 /* some applications make this assumption on the DC's origin */
1630 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1631 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1632 odaction, FALSE);
1633 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1635 /* Draw the popup-menu arrow */
1636 if (lpitem->fType & MF_POPUP)
1637 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1638 arrow_bitmap_height);
1639 rect.left += 4;
1640 if( !(menu->dwStyle & MNS_NOCHECK))
1641 rect.left += check_bitmap_width;
1642 rect.right -= arrow_bitmap_width;
1644 else if( lpitem->hbmpItem)
1645 { /* Draw the bitmap */
1646 POINT origorg;
1648 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1649 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1650 odaction, menuBar);
1651 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1653 /* process text if present */
1654 if (lpitem->text)
1656 register int i;
1657 HFONT hfontOld = 0;
1659 UINT uFormat = (menuBar) ?
1660 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1661 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1663 if( !(menu->dwStyle & MNS_CHECKORBMP))
1664 rect.left += menu->textOffset;
1666 if ( lpitem->fState & MFS_DEFAULT )
1668 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1671 if (menuBar) {
1672 if( lpitem->hbmpItem)
1673 rect.left += lpitem->bmpsize.cx;
1674 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1675 rect.left += menucharsize.cx;
1676 rect.right -= menucharsize.cx;
1679 for (i = 0; lpitem->text[i]; i++)
1680 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1681 break;
1683 if(lpitem->fState & MF_GRAYED)
1685 if (!(lpitem->fState & MF_HILITE) )
1687 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1688 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1689 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1690 --rect.left; --rect.top; --rect.right; --rect.bottom;
1692 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1695 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1697 /* paint the shortcut text */
1698 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1700 if (lpitem->text[i] == '\t')
1702 rect.left = lpitem->xTab;
1703 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1705 else
1707 rect.right = lpitem->xTab;
1708 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1711 if(lpitem->fState & MF_GRAYED)
1713 if (!(lpitem->fState & MF_HILITE) )
1715 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1716 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1717 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1718 --rect.left; --rect.top; --rect.right; --rect.bottom;
1720 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1722 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1725 if (hfontOld)
1726 SelectObject (hdc, hfontOld);
1731 /***********************************************************************
1732 * MENU_DrawPopupMenu
1734 * Paint a popup menu.
1736 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1738 HBRUSH hPrevBrush = 0;
1739 RECT rect;
1741 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1743 GetClientRect( hwnd, &rect );
1745 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1746 && (SelectObject( hdc, get_menu_font(FALSE))))
1748 HPEN hPrevPen;
1750 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1752 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1753 if( hPrevPen )
1755 POPUPMENU *menu;
1756 BOOL flat_menu = FALSE;
1758 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1759 if (flat_menu)
1760 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1761 else
1762 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1764 if( (menu = MENU_GetMenu( hmenu )))
1766 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1767 /* draw menu items */
1768 if( menu->nItems)
1770 MENUITEM *item;
1771 UINT u;
1773 item = menu->items;
1774 for( u = menu->nItems; u > 0; u--, item++)
1775 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1776 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1778 /* draw scroll arrows */
1779 if (menu->bScrolling)
1780 MENU_DrawScrollArrows(menu, hdc);
1782 } else
1784 SelectObject( hdc, hPrevBrush );
1789 /***********************************************************************
1790 * MENU_DrawMenuBar
1792 * Paint a menu bar. Returns the height of the menu bar.
1793 * called from [windows/nonclient.c]
1795 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1796 BOOL suppress_draw)
1798 LPPOPUPMENU lppop;
1799 HFONT hfontOld = 0;
1800 HMENU hMenu = GetMenu(hwnd);
1802 lppop = MENU_GetMenu( hMenu );
1803 if (lppop == NULL || lprect == NULL)
1805 return GetSystemMetrics(SM_CYMENU);
1808 if (suppress_draw)
1810 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1812 if (lppop->Height == 0)
1813 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1815 lprect->bottom = lprect->top + lppop->Height;
1817 if (hfontOld) SelectObject( hDC, hfontOld);
1818 return lppop->Height;
1820 else
1821 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1825 /***********************************************************************
1826 * MENU_ShowPopup
1828 * Display a popup menu.
1830 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1831 INT x, INT y, INT xanchor, INT yanchor )
1833 POPUPMENU *menu;
1834 INT width, height;
1835 POINT pt;
1836 HMONITOR monitor;
1837 MONITORINFO info;
1838 DWORD ex_style = 0;
1840 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1841 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1843 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1844 if (menu->FocusedItem != NO_SELECTED_ITEM)
1846 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1847 menu->FocusedItem = NO_SELECTED_ITEM;
1850 /* store the owner for DrawItem */
1851 menu->hwndOwner = hwndOwner;
1853 menu->nScrollPos = 0;
1854 MENU_PopupMenuCalcSize( menu );
1856 /* adjust popup menu pos so that it fits within the desktop */
1858 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1859 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1861 /* FIXME: should use item rect */
1862 pt.x = x;
1863 pt.y = y;
1864 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1865 info.cbSize = sizeof(info);
1866 GetMonitorInfoW( monitor, &info );
1868 if (flags & TPM_LAYOUTRTL)
1870 ex_style = WS_EX_LAYOUTRTL;
1871 flags ^= TPM_RIGHTALIGN;
1874 if( flags & TPM_RIGHTALIGN ) x -= width;
1875 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1877 if( flags & TPM_BOTTOMALIGN ) y -= height;
1878 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1880 if( x + width > info.rcWork.right)
1882 if( xanchor && x >= width - xanchor )
1883 x -= width - xanchor;
1885 if( x + width > info.rcWork.right)
1886 x = info.rcWork.right - width;
1888 if( x < info.rcWork.left ) x = info.rcWork.left;
1890 if( y + height > info.rcWork.bottom)
1892 if( yanchor && y >= height + yanchor )
1893 y -= height + yanchor;
1895 if( y + height > info.rcWork.bottom)
1896 y = info.rcWork.bottom - height;
1898 if( y < info.rcWork.top ) y = info.rcWork.top;
1900 /* NOTE: In Windows, top menu popup is not owned. */
1901 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1902 WS_POPUP, x, y, width, height,
1903 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1904 (LPVOID)hmenu );
1905 if( !menu->hWnd ) return FALSE;
1906 if (!top_popup) {
1907 top_popup = menu->hWnd;
1908 top_popup_hmenu = hmenu;
1910 /* Display the window */
1912 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1913 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1914 UpdateWindow( menu->hWnd );
1915 return TRUE;
1919 /***********************************************************************
1920 * MENU_EnsureMenuItemVisible
1922 static void
1923 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1925 if (lppop->bScrolling)
1927 MENUITEM *item = &lppop->items[wIndex];
1928 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1929 UINT nOldPos = lppop->nScrollPos;
1930 RECT rc;
1931 UINT arrow_bitmap_height;
1932 BITMAP bmp;
1934 GetClientRect(lppop->hWnd, &rc);
1936 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1937 arrow_bitmap_height = bmp.bmHeight;
1939 rc.top += arrow_bitmap_height;
1940 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1942 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1943 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1946 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1947 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1948 MENU_DrawScrollArrows(lppop, hdc);
1950 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1952 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1953 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1954 MENU_DrawScrollArrows(lppop, hdc);
1960 /***********************************************************************
1961 * MENU_SelectItem
1963 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1964 BOOL sendMenuSelect, HMENU topmenu )
1966 LPPOPUPMENU lppop;
1967 HDC hdc;
1969 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1971 lppop = MENU_GetMenu( hmenu );
1972 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1974 if (lppop->FocusedItem == wIndex) return;
1975 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1976 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1977 if (!top_popup) {
1978 top_popup = lppop->hWnd;
1979 top_popup_hmenu = hmenu;
1982 SelectObject( hdc, get_menu_font(FALSE));
1984 /* Clear previous highlighted item */
1985 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1987 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1988 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1989 lppop->Height, !(lppop->wFlags & MF_POPUP),
1990 ODA_SELECT );
1993 /* Highlight new item (if any) */
1994 lppop->FocusedItem = wIndex;
1995 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1997 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1998 lppop->items[wIndex].fState |= MF_HILITE;
1999 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2000 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2001 &lppop->items[wIndex], lppop->Height,
2002 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2004 if (sendMenuSelect)
2006 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2007 SendMessageW( hwndOwner, WM_MENUSELECT,
2008 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2009 ip->fType | ip->fState |
2010 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2013 else if (sendMenuSelect) {
2014 if(topmenu){
2015 int pos;
2016 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2017 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2018 MENUITEM *ip = &ptm->items[pos];
2019 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2020 ip->fType | ip->fState |
2021 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2025 ReleaseDC( lppop->hWnd, hdc );
2029 /***********************************************************************
2030 * MENU_MoveSelection
2032 * Moves currently selected item according to the offset parameter.
2033 * If there is no selection then it should select the last item if
2034 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2036 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2038 INT i;
2039 POPUPMENU *menu;
2041 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2043 menu = MENU_GetMenu( hmenu );
2044 if ((!menu) || (!menu->items)) return;
2046 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2048 if( menu->nItems == 1 ) return; else
2049 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2050 ; i += offset)
2051 if (!(menu->items[i].fType & MF_SEPARATOR))
2053 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2054 return;
2058 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2059 i >= 0 && i < menu->nItems ; i += offset)
2060 if (!(menu->items[i].fType & MF_SEPARATOR))
2062 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2063 return;
2068 /**********************************************************************
2069 * MENU_InsertItem
2071 * Insert (allocate) a new item into a menu.
2073 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2075 MENUITEM *newItems;
2076 POPUPMENU *menu;
2078 if (!(menu = MENU_GetMenu(hMenu)))
2079 return NULL;
2081 /* Find where to insert new item */
2083 if (flags & MF_BYPOSITION) {
2084 if (pos > menu->nItems)
2085 pos = menu->nItems;
2086 } else {
2087 if (!MENU_FindItem( &hMenu, &pos, flags ))
2088 pos = menu->nItems;
2089 else {
2090 if (!(menu = MENU_GetMenu( hMenu )))
2091 return NULL;
2095 /* Make sure that MDI system buttons stay on the right side.
2096 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2097 * regardless of their id.
2099 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2100 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2101 pos--;
2103 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2105 /* Create new items array */
2107 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2108 if (!newItems)
2110 WARN("allocation failed\n" );
2111 return NULL;
2113 if (menu->nItems > 0)
2115 /* Copy the old array into the new one */
2116 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2117 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2118 (menu->nItems-pos)*sizeof(MENUITEM) );
2119 HeapFree( GetProcessHeap(), 0, menu->items );
2121 menu->items = newItems;
2122 menu->nItems++;
2123 memset( &newItems[pos], 0, sizeof(*newItems) );
2124 menu->Height = 0; /* force size recalculate */
2125 return &newItems[pos];
2129 /**********************************************************************
2130 * MENU_ParseResource
2132 * Parse a standard menu resource and add items to the menu.
2133 * Return a pointer to the end of the resource.
2135 * NOTE: flags is equivalent to the mtOption field
2137 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2139 WORD flags, id = 0;
2140 LPCWSTR str;
2141 BOOL end_flag;
2145 flags = GET_WORD(res);
2146 end_flag = flags & MF_END;
2147 /* Remove MF_END because it has the same value as MF_HILITE */
2148 flags &= ~MF_END;
2149 res += sizeof(WORD);
2150 if (!(flags & MF_POPUP))
2152 id = GET_WORD(res);
2153 res += sizeof(WORD);
2155 str = (LPCWSTR)res;
2156 res += (strlenW(str) + 1) * sizeof(WCHAR);
2157 if (flags & MF_POPUP)
2159 HMENU hSubMenu = CreatePopupMenu();
2160 if (!hSubMenu) return NULL;
2161 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2162 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2164 else /* Not a popup */
2166 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2168 } while (!end_flag);
2169 return res;
2173 /**********************************************************************
2174 * MENUEX_ParseResource
2176 * Parse an extended menu resource and add items to the menu.
2177 * Return a pointer to the end of the resource.
2179 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2181 WORD resinfo;
2182 do {
2183 MENUITEMINFOW mii;
2185 mii.cbSize = sizeof(mii);
2186 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2187 mii.fType = GET_DWORD(res);
2188 res += sizeof(DWORD);
2189 mii.fState = GET_DWORD(res);
2190 res += sizeof(DWORD);
2191 mii.wID = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2194 res += sizeof(WORD);
2195 /* Align the text on a word boundary. */
2196 res += (~((UINT_PTR)res - 1)) & 1;
2197 mii.dwTypeData = (LPWSTR) res;
2198 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2199 /* Align the following fields on a dword boundary. */
2200 res += (~((UINT_PTR)res - 1)) & 3;
2202 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2203 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2205 if (resinfo & 1) { /* Pop-up? */
2206 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2207 res += sizeof(DWORD);
2208 mii.hSubMenu = CreatePopupMenu();
2209 if (!mii.hSubMenu)
2210 return NULL;
2211 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2212 DestroyMenu(mii.hSubMenu);
2213 return NULL;
2215 mii.fMask |= MIIM_SUBMENU;
2216 mii.fType |= MF_POPUP;
2218 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2220 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2221 mii.wID, mii.fType);
2222 mii.fType |= MF_SEPARATOR;
2224 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2225 } while (!(resinfo & MF_END));
2226 return res;
2230 /***********************************************************************
2231 * MENU_GetSubPopup
2233 * Return the handle of the selected sub-popup menu (if any).
2235 static HMENU MENU_GetSubPopup( HMENU hmenu )
2237 POPUPMENU *menu;
2238 MENUITEM *item;
2240 menu = MENU_GetMenu( hmenu );
2242 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2244 item = &menu->items[menu->FocusedItem];
2245 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2246 return item->hSubMenu;
2247 return 0;
2251 /***********************************************************************
2252 * MENU_HideSubPopups
2254 * Hide the sub-popup menus of this menu.
2256 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2257 BOOL sendMenuSelect, UINT wFlags )
2259 POPUPMENU *menu = MENU_GetMenu( hmenu );
2261 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2263 if (menu && top_popup)
2265 HMENU hsubmenu;
2266 POPUPMENU *submenu;
2267 MENUITEM *item;
2269 if (menu->FocusedItem != NO_SELECTED_ITEM)
2271 item = &menu->items[menu->FocusedItem];
2272 if (!(item->fType & MF_POPUP) ||
2273 !(item->fState & MF_MOUSESELECT)) return;
2274 item->fState &= ~MF_MOUSESELECT;
2275 hsubmenu = item->hSubMenu;
2276 } else return;
2278 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2279 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2280 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2281 DestroyWindow( submenu->hWnd );
2282 submenu->hWnd = 0;
2284 if (!(wFlags & TPM_NONOTIFY))
2285 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2286 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2291 /***********************************************************************
2292 * MENU_ShowSubPopup
2294 * Display the sub-menu of the selected item of this menu.
2295 * Return the handle of the submenu, or hmenu if no submenu to display.
2297 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2298 BOOL selectFirst, UINT wFlags )
2300 RECT rect;
2301 POPUPMENU *menu;
2302 MENUITEM *item;
2303 HDC hdc;
2305 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2307 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2309 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2311 item = &menu->items[menu->FocusedItem];
2312 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2313 return hmenu;
2315 /* message must be sent before using item,
2316 because nearly everything may be changed by the application ! */
2318 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2319 if (!(wFlags & TPM_NONOTIFY))
2320 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2321 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2323 item = &menu->items[menu->FocusedItem];
2324 rect = item->rect;
2326 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2327 if (!(item->fState & MF_HILITE))
2329 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2330 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2332 SelectObject( hdc, get_menu_font(FALSE));
2334 item->fState |= MF_HILITE;
2335 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2336 ReleaseDC( menu->hWnd, hdc );
2338 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2339 item->rect = rect;
2341 item->fState |= MF_MOUSESELECT;
2343 if (IS_SYSTEM_MENU(menu))
2345 MENU_InitSysMenuPopup(item->hSubMenu,
2346 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2347 GetClassLongW( menu->hWnd, GCL_STYLE));
2349 NC_GetSysPopupPos( menu->hWnd, &rect );
2350 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2351 rect.top = rect.bottom;
2352 rect.right = GetSystemMetrics(SM_CXSIZE);
2353 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2355 else
2357 GetWindowRect( menu->hWnd, &rect );
2358 if (menu->wFlags & MF_POPUP)
2360 RECT rc = item->rect;
2362 MENU_AdjustMenuItemRect(menu, &rc);
2364 /* The first item in the popup menu has to be at the
2365 same y position as the focused menu item */
2366 if (wFlags & TPM_LAYOUTRTL)
2367 rect.left += GetSystemMetrics(SM_CXBORDER);
2368 else
2369 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2370 rect.top += rc.top - MENU_TOP_MARGIN;
2371 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2372 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2373 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2375 else
2377 if (wFlags & TPM_LAYOUTRTL)
2378 rect.left = rect.right - item->rect.left;
2379 else
2380 rect.left += item->rect.left;
2381 rect.top += item->rect.bottom;
2382 rect.right = item->rect.right - item->rect.left;
2383 rect.bottom = item->rect.bottom - item->rect.top;
2387 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2388 rect.left, rect.top, rect.right, rect.bottom );
2389 if (selectFirst)
2390 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2391 return item->hSubMenu;
2396 /**********************************************************************
2397 * MENU_IsMenuActive
2399 HWND MENU_IsMenuActive(void)
2401 return top_popup;
2404 /**********************************************************************
2405 * MENU_EndMenu
2407 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2409 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2411 void MENU_EndMenu( HWND hwnd )
2413 POPUPMENU *menu;
2414 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2415 if (menu && hwnd == menu->hwndOwner) EndMenu();
2418 /***********************************************************************
2419 * MENU_PtMenu
2421 * Walks menu chain trying to find a menu pt maps to.
2423 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2425 POPUPMENU *menu = MENU_GetMenu( hMenu );
2426 UINT item = menu->FocusedItem;
2427 HMENU ret;
2429 /* try subpopup first (if any) */
2430 ret = (item != NO_SELECTED_ITEM &&
2431 (menu->items[item].fType & MF_POPUP) &&
2432 (menu->items[item].fState & MF_MOUSESELECT))
2433 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2435 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2437 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2438 if( menu->wFlags & MF_POPUP )
2440 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2442 else if (ht == HTSYSMENU)
2443 ret = get_win_sys_menu( menu->hWnd );
2444 else if (ht == HTMENU)
2445 ret = GetMenu( menu->hWnd );
2447 return ret;
2450 /***********************************************************************
2451 * MENU_ExecFocusedItem
2453 * Execute a menu item (for instance when user pressed Enter).
2454 * Return the wID of the executed item. Otherwise, -1 indicating
2455 * that no menu item was executed, -2 if a popup is shown;
2456 * Have to receive the flags for the TrackPopupMenu options to avoid
2457 * sending unwanted message.
2460 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2462 MENUITEM *item;
2463 POPUPMENU *menu = MENU_GetMenu( hMenu );
2465 TRACE("%p hmenu=%p\n", pmt, hMenu);
2467 if (!menu || !menu->nItems ||
2468 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2470 item = &menu->items[menu->FocusedItem];
2472 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2474 if (!(item->fType & MF_POPUP))
2476 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2478 /* If TPM_RETURNCMD is set you return the id, but
2479 do not send a message to the owner */
2480 if(!(wFlags & TPM_RETURNCMD))
2482 if( menu->wFlags & MF_SYSMENU )
2483 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2484 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2485 else
2487 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2488 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2490 if (dwStyle & MNS_NOTIFYBYPOS)
2491 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2492 (LPARAM)hMenu);
2493 else
2494 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2497 return item->wID;
2500 else
2502 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2503 return -2;
2506 return -1;
2509 /***********************************************************************
2510 * MENU_SwitchTracking
2512 * Helper function for menu navigation routines.
2514 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2516 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2517 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2519 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2521 if( pmt->hTopMenu != hPtMenu &&
2522 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2524 /* both are top level menus (system and menu-bar) */
2525 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2526 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2527 pmt->hTopMenu = hPtMenu;
2529 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2530 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2534 /***********************************************************************
2535 * MENU_ButtonDown
2537 * Return TRUE if we can go on with menu tracking.
2539 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2541 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2543 if (hPtMenu)
2545 UINT id = 0;
2546 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2547 MENUITEM *item;
2549 if( IS_SYSTEM_MENU(ptmenu) )
2550 item = ptmenu->items;
2551 else
2552 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2554 if( item )
2556 if( ptmenu->FocusedItem != id )
2557 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2559 /* If the popup menu is not already "popped" */
2560 if(!(item->fState & MF_MOUSESELECT ))
2562 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2565 return TRUE;
2567 /* Else the click was on the menu bar, finish the tracking */
2569 return FALSE;
2572 /***********************************************************************
2573 * MENU_ButtonUp
2575 * Return the value of MENU_ExecFocusedItem if
2576 * the selected item was not a popup. Else open the popup.
2577 * A -1 return value indicates that we go on with menu tracking.
2580 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2582 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2584 if (hPtMenu)
2586 UINT id = 0;
2587 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2588 MENUITEM *item;
2590 if( IS_SYSTEM_MENU(ptmenu) )
2591 item = ptmenu->items;
2592 else
2593 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2595 if( item && (ptmenu->FocusedItem == id ))
2597 debug_print_menuitem ("FocusedItem: ", item, "");
2599 if( !(item->fType & MF_POPUP) )
2601 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2602 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2603 return executedMenuId;
2606 /* If we are dealing with the top-level menu */
2607 /* and this is a click on an already "popped" item: */
2608 /* Stop the menu tracking and close the opened submenus */
2609 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2610 return 0;
2612 ptmenu->bTimeToHide = TRUE;
2614 return -1;
2618 /***********************************************************************
2619 * MENU_MouseMove
2621 * Return TRUE if we can go on with menu tracking.
2623 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2625 UINT id = NO_SELECTED_ITEM;
2626 POPUPMENU *ptmenu = NULL;
2628 if( hPtMenu )
2630 ptmenu = MENU_GetMenu( hPtMenu );
2631 if( IS_SYSTEM_MENU(ptmenu) )
2632 id = 0;
2633 else
2634 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2637 if( id == NO_SELECTED_ITEM )
2639 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2640 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2643 else if( ptmenu->FocusedItem != id )
2645 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2646 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2648 return TRUE;
2652 /***********************************************************************
2653 * MENU_DoNextMenu
2655 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2657 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2659 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2660 BOOL atEnd = FALSE;
2662 /* When skipping left, we need to do something special after the
2663 first menu. */
2664 if (vk == VK_LEFT && menu->FocusedItem == 0)
2666 atEnd = TRUE;
2668 /* When skipping right, for the non-system menu, we need to
2669 handle the last non-special menu item (ie skip any window
2670 icons such as MDI maximize, restore or close) */
2671 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2673 UINT i = menu->FocusedItem + 1;
2674 while (i < menu->nItems) {
2675 if ((menu->items[i].wID >= SC_SIZE &&
2676 menu->items[i].wID <= SC_RESTORE)) {
2677 i++;
2678 } else break;
2680 if (i == menu->nItems) {
2681 atEnd = TRUE;
2684 /* When skipping right, we need to cater for the system menu */
2685 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2687 if (menu->FocusedItem == (menu->nItems - 1)) {
2688 atEnd = TRUE;
2692 if( atEnd )
2694 MDINEXTMENU next_menu;
2695 HMENU hNewMenu;
2696 HWND hNewWnd;
2697 UINT id = 0;
2699 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2700 next_menu.hmenuNext = 0;
2701 next_menu.hwndNext = 0;
2702 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2704 TRACE("%p [%p] -> %p [%p]\n",
2705 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2707 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2709 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2710 hNewWnd = pmt->hOwnerWnd;
2711 if( IS_SYSTEM_MENU(menu) )
2713 /* switch to the menu bar */
2715 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2717 if( vk == VK_LEFT )
2719 menu = MENU_GetMenu( hNewMenu );
2720 id = menu->nItems - 1;
2722 /* Skip backwards over any system predefined icons,
2723 eg. MDI close, restore etc icons */
2724 while ((id > 0) &&
2725 (menu->items[id].wID >= SC_SIZE &&
2726 menu->items[id].wID <= SC_RESTORE)) id--;
2729 else if (style & WS_SYSMENU )
2731 /* switch to the system menu */
2732 hNewMenu = get_win_sys_menu( hNewWnd );
2734 else return FALSE;
2736 else /* application returned a new menu to switch to */
2738 hNewMenu = next_menu.hmenuNext;
2739 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2741 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2743 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2745 if (style & WS_SYSMENU &&
2746 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2748 /* get the real system menu */
2749 hNewMenu = get_win_sys_menu(hNewWnd);
2751 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2753 /* FIXME: Not sure what to do here;
2754 * perhaps try to track hNewMenu as a popup? */
2756 TRACE(" -- got confused.\n");
2757 return FALSE;
2760 else return FALSE;
2763 if( hNewMenu != pmt->hTopMenu )
2765 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2766 FALSE, 0 );
2767 if( pmt->hCurrentMenu != pmt->hTopMenu )
2768 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2771 if( hNewWnd != pmt->hOwnerWnd )
2773 pmt->hOwnerWnd = hNewWnd;
2774 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2777 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2778 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2780 return TRUE;
2782 return FALSE;
2785 /***********************************************************************
2786 * MENU_SuspendPopup
2788 * The idea is not to show the popup if the next input message is
2789 * going to hide it anyway.
2791 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2793 MSG msg;
2795 msg.hwnd = pmt->hOwnerWnd;
2797 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2798 pmt->trackFlags |= TF_SKIPREMOVE;
2800 switch( uMsg )
2802 case WM_KEYDOWN:
2803 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2804 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2806 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2807 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2808 if( msg.message == WM_KEYDOWN &&
2809 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2811 pmt->trackFlags |= TF_SUSPENDPOPUP;
2812 return TRUE;
2815 break;
2818 /* failures go through this */
2819 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2820 return FALSE;
2823 /***********************************************************************
2824 * MENU_KeyEscape
2826 * Handle a VK_ESCAPE key event in a menu.
2828 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2830 BOOL bEndMenu = TRUE;
2832 if (pmt->hCurrentMenu != pmt->hTopMenu)
2834 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2836 if (menu->wFlags & MF_POPUP)
2838 HMENU hmenutmp, hmenuprev;
2840 hmenuprev = hmenutmp = pmt->hTopMenu;
2842 /* close topmost popup */
2843 while (hmenutmp != pmt->hCurrentMenu)
2845 hmenuprev = hmenutmp;
2846 hmenutmp = MENU_GetSubPopup( hmenuprev );
2849 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2850 pmt->hCurrentMenu = hmenuprev;
2851 bEndMenu = FALSE;
2855 return bEndMenu;
2858 /***********************************************************************
2859 * MENU_KeyLeft
2861 * Handle a VK_LEFT key event in a menu.
2863 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2865 POPUPMENU *menu;
2866 HMENU hmenutmp, hmenuprev;
2867 UINT prevcol;
2869 hmenuprev = hmenutmp = pmt->hTopMenu;
2870 menu = MENU_GetMenu( hmenutmp );
2872 /* Try to move 1 column left (if possible) */
2873 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2874 NO_SELECTED_ITEM ) {
2876 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2877 prevcol, TRUE, 0 );
2878 return;
2881 /* close topmost popup */
2882 while (hmenutmp != pmt->hCurrentMenu)
2884 hmenuprev = hmenutmp;
2885 hmenutmp = MENU_GetSubPopup( hmenuprev );
2888 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2889 pmt->hCurrentMenu = hmenuprev;
2891 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2893 /* move menu bar selection if no more popups are left */
2895 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2896 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2898 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2900 /* A sublevel menu was displayed - display the next one
2901 * unless there is another displacement coming up */
2903 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2904 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2905 pmt->hTopMenu, TRUE, wFlags);
2911 /***********************************************************************
2912 * MENU_KeyRight
2914 * Handle a VK_RIGHT key event in a menu.
2916 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2918 HMENU hmenutmp;
2919 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2920 UINT nextcol;
2922 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2923 pmt->hCurrentMenu,
2924 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2925 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2927 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2929 /* If already displaying a popup, try to display sub-popup */
2931 hmenutmp = pmt->hCurrentMenu;
2932 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2934 /* if subpopup was displayed then we are done */
2935 if (hmenutmp != pmt->hCurrentMenu) return;
2938 /* Check to see if there's another column */
2939 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2940 NO_SELECTED_ITEM ) {
2941 TRACE("Going to %d.\n", nextcol );
2942 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2943 nextcol, TRUE, 0 );
2944 return;
2947 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2949 if( pmt->hCurrentMenu != pmt->hTopMenu )
2951 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2952 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2953 } else hmenutmp = 0;
2955 /* try to move to the next item */
2956 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2957 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2959 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2960 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2961 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2962 pmt->hTopMenu, TRUE, wFlags);
2966 static void CALLBACK release_capture( BOOL __normal )
2968 set_capture_window( 0, GUI_INMENUMODE, NULL );
2971 /***********************************************************************
2972 * MENU_TrackMenu
2974 * Menu tracking code.
2976 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2977 HWND hwnd, const RECT *lprect )
2979 MSG msg;
2980 POPUPMENU *menu;
2981 BOOL fRemove;
2982 INT executedMenuId = -1;
2983 MTRACKER mt;
2984 BOOL enterIdleSent = FALSE;
2985 HWND capture_win;
2987 mt.trackFlags = 0;
2988 mt.hCurrentMenu = hmenu;
2989 mt.hTopMenu = hmenu;
2990 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2991 mt.pt.x = x;
2992 mt.pt.y = y;
2994 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2995 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2997 fEndMenu = FALSE;
2998 if (!(menu = MENU_GetMenu( hmenu )))
3000 WARN("Invalid menu handle %p\n", hmenu);
3001 SetLastError(ERROR_INVALID_MENU_HANDLE);
3002 return FALSE;
3005 if (wFlags & TPM_BUTTONDOWN)
3007 /* Get the result in order to start the tracking or not */
3008 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3009 fEndMenu = !fRemove;
3012 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3014 /* owner may not be visible when tracking a popup, so use the menu itself */
3015 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3016 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3018 __TRY while (!fEndMenu)
3020 menu = MENU_GetMenu( mt.hCurrentMenu );
3021 if (!menu) /* sometimes happens if I do a window manager close */
3022 break;
3024 /* we have to keep the message in the queue until it's
3025 * clear that menu loop is not over yet. */
3027 for (;;)
3029 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3031 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3032 /* remove the message from the queue */
3033 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3035 else
3037 if (!enterIdleSent)
3039 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3040 enterIdleSent = TRUE;
3041 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3043 WaitMessage();
3047 /* check if EndMenu() tried to cancel us, by posting this message */
3048 if(msg.message == WM_CANCELMODE)
3050 /* we are now out of the loop */
3051 fEndMenu = TRUE;
3053 /* remove the message from the queue */
3054 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3056 /* break out of internal loop, ala ESCAPE */
3057 break;
3060 TranslateMessage( &msg );
3061 mt.pt = msg.pt;
3063 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3064 enterIdleSent=FALSE;
3066 fRemove = FALSE;
3067 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3070 * Use the mouse coordinates in lParam instead of those in the MSG
3071 * struct to properly handle synthetic messages. They are already
3072 * in screen coordinates.
3074 mt.pt.x = (short)LOWORD(msg.lParam);
3075 mt.pt.y = (short)HIWORD(msg.lParam);
3077 /* Find a menu for this mouse event */
3078 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3080 switch(msg.message)
3082 /* no WM_NC... messages in captured state */
3084 case WM_RBUTTONDBLCLK:
3085 case WM_RBUTTONDOWN:
3086 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3087 /* fall through */
3088 case WM_LBUTTONDBLCLK:
3089 case WM_LBUTTONDOWN:
3090 /* If the message belongs to the menu, removes it from the queue */
3091 /* Else, end menu tracking */
3092 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3093 fEndMenu = !fRemove;
3094 break;
3096 case WM_RBUTTONUP:
3097 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3098 /* fall through */
3099 case WM_LBUTTONUP:
3100 /* Check if a menu was selected by the mouse */
3101 if (hmenu)
3103 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3104 TRACE("executedMenuId %d\n", executedMenuId);
3106 /* End the loop if executedMenuId is an item ID */
3107 /* or if the job was done (executedMenuId = 0). */
3108 fEndMenu = fRemove = (executedMenuId != -1);
3110 /* No menu was selected by the mouse */
3111 /* if the function was called by TrackPopupMenu, continue
3112 with the menu tracking. If not, stop it */
3113 else
3114 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3116 break;
3118 case WM_MOUSEMOVE:
3119 /* the selected menu item must be changed every time */
3120 /* the mouse moves. */
3122 if (hmenu)
3123 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3125 } /* switch(msg.message) - mouse */
3127 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3129 fRemove = TRUE; /* Keyboard messages are always removed */
3130 switch(msg.message)
3132 case WM_KEYDOWN:
3133 case WM_SYSKEYDOWN:
3134 switch(msg.wParam)
3136 case VK_MENU:
3137 case VK_F10:
3138 fEndMenu = TRUE;
3139 break;
3141 case VK_HOME:
3142 case VK_END:
3143 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3144 NO_SELECTED_ITEM, FALSE, 0 );
3145 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3146 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3147 break;
3149 case VK_UP:
3150 case VK_DOWN: /* If on menu bar, pull-down the menu */
3152 menu = MENU_GetMenu( mt.hCurrentMenu );
3153 if (!(menu->wFlags & MF_POPUP))
3154 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3155 else /* otherwise try to move selection */
3156 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3157 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3158 break;
3160 case VK_LEFT:
3161 MENU_KeyLeft( &mt, wFlags );
3162 break;
3164 case VK_RIGHT:
3165 MENU_KeyRight( &mt, wFlags );
3166 break;
3168 case VK_ESCAPE:
3169 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3170 break;
3172 case VK_F1:
3174 HELPINFO hi;
3175 hi.cbSize = sizeof(HELPINFO);
3176 hi.iContextType = HELPINFO_MENUITEM;
3177 if (menu->FocusedItem == NO_SELECTED_ITEM)
3178 hi.iCtrlId = 0;
3179 else
3180 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3181 hi.hItemHandle = hmenu;
3182 hi.dwContextId = menu->dwContextHelpID;
3183 hi.MousePos = msg.pt;
3184 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3185 break;
3188 default:
3189 break;
3191 break; /* WM_KEYDOWN */
3193 case WM_CHAR:
3194 case WM_SYSCHAR:
3196 UINT pos;
3198 if (msg.wParam == '\r' || msg.wParam == ' ')
3200 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3201 fEndMenu = (executedMenuId != -2);
3203 break;
3206 /* Hack to avoid control chars. */
3207 /* We will find a better way real soon... */
3208 if (msg.wParam < 32) break;
3210 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3211 LOWORD(msg.wParam), FALSE );
3212 if (pos == (UINT)-2) fEndMenu = TRUE;
3213 else if (pos == (UINT)-1) MessageBeep(0);
3214 else
3216 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3217 TRUE, 0 );
3218 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3219 fEndMenu = (executedMenuId != -2);
3222 break;
3223 } /* switch(msg.message) - kbd */
3225 else
3227 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3228 DispatchMessageW( &msg );
3229 continue;
3232 if (!fEndMenu) fRemove = TRUE;
3234 /* finally remove message from the queue */
3236 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3237 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3238 else mt.trackFlags &= ~TF_SKIPREMOVE;
3240 __FINALLY( release_capture )
3242 /* If dropdown is still painted and the close box is clicked on
3243 then the menu will be destroyed as part of the DispatchMessage above.
3244 This will then invalidate the menu handle in mt.hTopMenu. We should
3245 check for this first. */
3246 if( IsMenu( mt.hTopMenu ) )
3248 menu = MENU_GetMenu( mt.hTopMenu );
3250 if( IsWindow( mt.hOwnerWnd ) )
3252 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3254 if (menu && (menu->wFlags & MF_POPUP))
3256 DestroyWindow( menu->hWnd );
3257 menu->hWnd = 0;
3259 if (!(wFlags & TPM_NONOTIFY))
3260 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3261 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3263 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3264 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3267 /* Reset the variable for hiding menu */
3268 if( menu ) menu->bTimeToHide = FALSE;
3271 /* The return value is only used by TrackPopupMenu */
3272 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3273 if (executedMenuId == -1) executedMenuId = 0;
3274 return executedMenuId;
3277 /***********************************************************************
3278 * MENU_InitTracking
3280 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3282 POPUPMENU *menu;
3284 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3286 HideCaret(0);
3288 /* This makes the menus of applications built with Delphi work.
3289 * It also enables menus to be displayed in more than one window,
3290 * but there are some bugs left that need to be fixed in this case.
3292 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3294 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3295 if (!(wFlags & TPM_NONOTIFY))
3296 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3298 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3300 if (!(wFlags & TPM_NONOTIFY))
3302 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3303 /* If an app changed/recreated menu bar entries in WM_INITMENU
3304 * menu sizes will be recalculated once the menu created/shown.
3308 return TRUE;
3311 /***********************************************************************
3312 * MENU_ExitTracking
3314 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3316 TRACE("hwnd=%p\n", hWnd);
3318 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3319 ShowCaret(0);
3320 top_popup = 0;
3321 top_popup_hmenu = NULL;
3322 return TRUE;
3325 /***********************************************************************
3326 * MENU_TrackMouseMenuBar
3328 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3330 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3332 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3333 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3335 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3337 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3338 if (IsMenu(hMenu))
3340 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3341 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3342 MENU_ExitTracking(hWnd, FALSE);
3347 /***********************************************************************
3348 * MENU_TrackKbdMenuBar
3350 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3352 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3354 UINT uItem = NO_SELECTED_ITEM;
3355 HMENU hTrackMenu;
3356 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3358 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3360 /* find window that has a menu */
3362 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3363 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3365 /* check if we have to track a system menu */
3367 hTrackMenu = GetMenu( hwnd );
3368 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3370 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3371 hTrackMenu = get_win_sys_menu( hwnd );
3372 uItem = 0;
3373 wParam |= HTSYSMENU; /* prevent item lookup */
3375 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3377 if (!IsMenu( hTrackMenu )) return;
3379 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3381 if( wChar && wChar != ' ' )
3383 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3384 if ( uItem >= (UINT)(-2) )
3386 if( uItem == (UINT)(-1) ) MessageBeep(0);
3387 /* schedule end of menu tracking */
3388 wFlags |= TF_ENDMENU;
3389 goto track_menu;
3393 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3395 if (!(wParam & HTSYSMENU) || wChar == ' ')
3397 if( uItem == NO_SELECTED_ITEM )
3398 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3399 else
3400 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3403 track_menu:
3404 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3405 MENU_ExitTracking( hwnd, FALSE );
3408 /**********************************************************************
3409 * TrackPopupMenuEx (USER32.@)
3411 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3412 HWND hWnd, LPTPMPARAMS lpTpm )
3414 POPUPMENU *menu;
3415 BOOL ret = FALSE;
3417 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3418 hMenu, wFlags, x, y, hWnd, lpTpm,
3419 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3421 /* Parameter check */
3422 /* FIXME: this check is performed several times, here and in the called
3423 functions. That could be optimized */
3424 if (!(menu = MENU_GetMenu( hMenu )))
3426 SetLastError( ERROR_INVALID_MENU_HANDLE );
3427 return FALSE;
3430 if (IsWindow(menu->hWnd))
3432 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3433 return FALSE;
3436 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3438 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3439 if (!(wFlags & TPM_NONOTIFY))
3440 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3442 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3443 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3444 lpTpm ? &lpTpm->rcExclude : NULL );
3445 MENU_ExitTracking(hWnd, TRUE);
3447 return ret;
3450 /**********************************************************************
3451 * TrackPopupMenu (USER32.@)
3453 * Like the win32 API, the function return the command ID only if the
3454 * flag TPM_RETURNCMD is on.
3457 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3458 INT nReserved, HWND hWnd, const RECT *lpRect )
3460 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3463 /***********************************************************************
3464 * PopupMenuWndProc
3466 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3468 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3470 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3472 switch(message)
3474 case WM_CREATE:
3476 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3477 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3478 return 0;
3481 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3482 return MA_NOACTIVATE;
3484 case WM_PAINT:
3486 PAINTSTRUCT ps;
3487 BeginPaint( hwnd, &ps );
3488 MENU_DrawPopupMenu( hwnd, ps.hdc,
3489 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3490 EndPaint( hwnd, &ps );
3491 return 0;
3494 case WM_PRINTCLIENT:
3496 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3497 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3498 return 0;
3501 case WM_ERASEBKGND:
3502 return 1;
3504 case WM_DESTROY:
3505 /* zero out global pointer in case resident popup window was destroyed. */
3506 if (hwnd == top_popup) {
3507 top_popup = 0;
3508 top_popup_hmenu = NULL;
3510 break;
3512 case WM_SHOWWINDOW:
3514 if( wParam )
3516 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3518 else
3519 SetWindowLongPtrW( hwnd, 0, 0 );
3520 break;
3522 case MM_SETMENUHANDLE:
3523 SetWindowLongPtrW( hwnd, 0, wParam );
3524 break;
3526 case MM_GETMENUHANDLE:
3527 case MN_GETHMENU:
3528 return GetWindowLongPtrW( hwnd, 0 );
3530 default:
3531 return DefWindowProcW( hwnd, message, wParam, lParam );
3533 return 0;
3537 /***********************************************************************
3538 * MENU_GetMenuBarHeight
3540 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3542 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3543 INT orgX, INT orgY )
3545 HDC hdc;
3546 RECT rectBar;
3547 LPPOPUPMENU lppop;
3549 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3551 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3553 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3554 SelectObject( hdc, get_menu_font(FALSE));
3555 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3556 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3557 ReleaseDC( hwnd, hdc );
3558 return lppop->Height;
3562 /*******************************************************************
3563 * ChangeMenuA (USER32.@)
3565 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3566 UINT id, UINT flags )
3568 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3569 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3570 id, data );
3571 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3572 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3573 id, data );
3574 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3575 flags & MF_BYPOSITION ? pos : id,
3576 flags & ~MF_REMOVE );
3577 /* Default: MF_INSERT */
3578 return InsertMenuA( hMenu, pos, flags, id, data );
3582 /*******************************************************************
3583 * ChangeMenuW (USER32.@)
3585 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3586 UINT id, UINT flags )
3588 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3589 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3590 id, data );
3591 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3592 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3593 id, data );
3594 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3595 flags & MF_BYPOSITION ? pos : id,
3596 flags & ~MF_REMOVE );
3597 /* Default: MF_INSERT */
3598 return InsertMenuW( hMenu, pos, flags, id, data );
3602 /*******************************************************************
3603 * CheckMenuItem (USER32.@)
3605 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3607 MENUITEM *item;
3608 DWORD ret;
3610 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3611 ret = item->fState & MF_CHECKED;
3612 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3613 else item->fState &= ~MF_CHECKED;
3614 return ret;
3618 /**********************************************************************
3619 * EnableMenuItem (USER32.@)
3621 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3623 UINT oldflags;
3624 MENUITEM *item;
3625 POPUPMENU *menu;
3627 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3629 /* Get the Popupmenu to access the owner menu */
3630 if (!(menu = MENU_GetMenu(hMenu)))
3631 return (UINT)-1;
3633 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3634 return (UINT)-1;
3636 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3637 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3639 /* If the close item in the system menu change update the close button */
3640 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3642 if (menu->hSysMenuOwner != 0)
3644 RECT rc;
3645 POPUPMENU* parentMenu;
3647 /* Get the parent menu to access*/
3648 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3649 return (UINT)-1;
3651 /* Refresh the frame to reflect the change */
3652 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3653 rc.bottom = 0;
3654 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3658 return oldflags;
3662 /*******************************************************************
3663 * GetMenuStringA (USER32.@)
3665 INT WINAPI GetMenuStringA(
3666 HMENU hMenu, /* [in] menuhandle */
3667 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3668 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3669 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3670 UINT wFlags /* [in] MF_ flags */
3672 MENUITEM *item;
3674 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3675 if (str && nMaxSiz) str[0] = '\0';
3676 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3677 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3678 return 0;
3680 if (!item->text) return 0;
3681 if (!str || !nMaxSiz) return strlenW(item->text);
3682 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3683 str[nMaxSiz-1] = 0;
3684 TRACE("returning %s\n", debugstr_a(str));
3685 return strlen(str);
3689 /*******************************************************************
3690 * GetMenuStringW (USER32.@)
3692 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3693 LPWSTR str, INT nMaxSiz, UINT wFlags )
3695 MENUITEM *item;
3697 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3698 if (str && nMaxSiz) str[0] = '\0';
3699 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3700 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3701 return 0;
3703 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3704 if( !(item->text)) {
3705 str[0] = 0;
3706 return 0;
3708 lstrcpynW( str, item->text, nMaxSiz );
3709 TRACE("returning %s\n", debugstr_w(str));
3710 return strlenW(str);
3714 /**********************************************************************
3715 * HiliteMenuItem (USER32.@)
3717 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3718 UINT wHilite )
3720 LPPOPUPMENU menu;
3721 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3722 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3723 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3724 if (menu->FocusedItem == wItemID) return TRUE;
3725 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3726 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3727 return TRUE;
3731 /**********************************************************************
3732 * GetMenuState (USER32.@)
3734 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3736 MENUITEM *item;
3737 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3738 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3739 debug_print_menuitem (" item: ", item, "");
3740 if (item->fType & MF_POPUP)
3742 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3743 if (!menu) return -1;
3744 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3746 else
3748 /* We used to (from way back then) mask the result to 0xff. */
3749 /* I don't know why and it seems wrong as the documented */
3750 /* return flag MF_SEPARATOR is outside that mask. */
3751 return (item->fType | item->fState);
3756 /**********************************************************************
3757 * GetMenuItemCount (USER32.@)
3759 INT WINAPI GetMenuItemCount( HMENU hMenu )
3761 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3762 if (!menu) return -1;
3763 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3764 return menu->nItems;
3768 /**********************************************************************
3769 * GetMenuItemID (USER32.@)
3771 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3773 MENUITEM * lpmi;
3775 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3776 if (lpmi->fType & MF_POPUP) return -1;
3777 return lpmi->wID;
3782 /**********************************************************************
3783 * MENU_mnu2mnuii
3785 * Uses flags, id and text ptr, passed by InsertMenu() and
3786 * ModifyMenu() to setup a MenuItemInfo structure.
3788 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3789 LPMENUITEMINFOW pmii)
3791 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3792 pmii->cbSize = sizeof( MENUITEMINFOW);
3793 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3794 /* setting bitmap clears text and vice versa */
3795 if( IS_STRING_ITEM(flags)) {
3796 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3797 if( !str)
3798 flags |= MF_SEPARATOR;
3799 /* Item beginning with a backspace is a help item */
3800 /* FIXME: wrong place, this is only true in win16 */
3801 else if( *str == '\b') {
3802 flags |= MF_HELP;
3803 str++;
3805 pmii->dwTypeData = (LPWSTR)str;
3806 } else if( flags & MFT_BITMAP){
3807 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3808 pmii->hbmpItem = ULongToHandle(LOWORD(str));
3810 if( flags & MF_OWNERDRAW){
3811 pmii->fMask |= MIIM_DATA;
3812 pmii->dwItemData = (ULONG_PTR) str;
3814 if( flags & MF_POPUP) {
3815 pmii->fMask |= MIIM_SUBMENU;
3816 pmii->hSubMenu = (HMENU)id;
3818 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3819 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3820 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3821 pmii->wID = (UINT)id;
3825 /*******************************************************************
3826 * InsertMenuW (USER32.@)
3828 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3829 UINT_PTR id, LPCWSTR str )
3831 MENUITEM *item;
3832 MENUITEMINFOW mii;
3834 if (IS_STRING_ITEM(flags) && str)
3835 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3836 hMenu, pos, flags, id, debugstr_w(str) );
3837 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3838 hMenu, pos, flags, id, str );
3840 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3841 MENU_mnu2mnuii( flags, id, str, &mii);
3842 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3844 RemoveMenu( hMenu, pos, flags );
3845 return FALSE;
3848 item->hCheckBit = item->hUnCheckBit = 0;
3849 return TRUE;
3853 /*******************************************************************
3854 * InsertMenuA (USER32.@)
3856 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3857 UINT_PTR id, LPCSTR str )
3859 BOOL ret = FALSE;
3861 if (IS_STRING_ITEM(flags) && str)
3863 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3864 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3865 if (newstr)
3867 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3868 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3869 HeapFree( GetProcessHeap(), 0, newstr );
3871 return ret;
3873 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3877 /*******************************************************************
3878 * AppendMenuA (USER32.@)
3880 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3881 UINT_PTR id, LPCSTR data )
3883 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3887 /*******************************************************************
3888 * AppendMenuW (USER32.@)
3890 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3891 UINT_PTR id, LPCWSTR data )
3893 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3897 /**********************************************************************
3898 * RemoveMenu (USER32.@)
3900 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3902 LPPOPUPMENU menu;
3903 MENUITEM *item;
3905 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3906 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3907 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3909 /* Remove item */
3911 MENU_FreeItemData( item );
3913 if (--menu->nItems == 0)
3915 HeapFree( GetProcessHeap(), 0, menu->items );
3916 menu->items = NULL;
3918 else
3920 while(nPos < menu->nItems)
3922 *item = *(item+1);
3923 item++;
3924 nPos++;
3926 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3927 menu->nItems * sizeof(MENUITEM) );
3929 return TRUE;
3933 /**********************************************************************
3934 * DeleteMenu (USER32.@)
3936 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3938 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3939 if (!item) return FALSE;
3940 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3941 /* nPos is now the position of the item */
3942 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3943 return TRUE;
3947 /*******************************************************************
3948 * ModifyMenuW (USER32.@)
3950 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3951 UINT_PTR id, LPCWSTR str )
3953 MENUITEM *item;
3954 MENUITEMINFOW mii;
3956 if (IS_STRING_ITEM(flags))
3957 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3958 else
3959 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3961 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3962 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3963 MENU_mnu2mnuii( flags, id, str, &mii);
3964 return SetMenuItemInfo_common( item, &mii, TRUE);
3968 /*******************************************************************
3969 * ModifyMenuA (USER32.@)
3971 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3972 UINT_PTR id, LPCSTR str )
3974 BOOL ret = FALSE;
3976 if (IS_STRING_ITEM(flags) && str)
3978 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3979 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3980 if (newstr)
3982 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3983 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3984 HeapFree( GetProcessHeap(), 0, newstr );
3986 return ret;
3988 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3992 /**********************************************************************
3993 * CreatePopupMenu (USER32.@)
3995 HMENU WINAPI CreatePopupMenu(void)
3997 HMENU hmenu;
3998 POPUPMENU *menu;
4000 if (!(hmenu = CreateMenu())) return 0;
4001 menu = MENU_GetMenu( hmenu );
4002 menu->wFlags |= MF_POPUP;
4003 menu->bTimeToHide = FALSE;
4004 return hmenu;
4008 /**********************************************************************
4009 * GetMenuCheckMarkDimensions (USER.417)
4010 * GetMenuCheckMarkDimensions (USER32.@)
4012 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4014 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4018 /**********************************************************************
4019 * SetMenuItemBitmaps (USER32.@)
4021 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4022 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4024 MENUITEM *item;
4026 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4028 if (!hNewCheck && !hNewUnCheck)
4030 item->fState &= ~MF_USECHECKBITMAPS;
4032 else /* Install new bitmaps */
4034 item->hCheckBit = hNewCheck;
4035 item->hUnCheckBit = hNewUnCheck;
4036 item->fState |= MF_USECHECKBITMAPS;
4038 return TRUE;
4042 /**********************************************************************
4043 * CreateMenu (USER32.@)
4045 HMENU WINAPI CreateMenu(void)
4047 HMENU hMenu;
4048 LPPOPUPMENU menu;
4050 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4051 menu->FocusedItem = NO_SELECTED_ITEM;
4052 menu->bTimeToHide = FALSE;
4054 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4056 TRACE("return %p\n", hMenu );
4058 return hMenu;
4062 /**********************************************************************
4063 * DestroyMenu (USER32.@)
4065 BOOL WINAPI DestroyMenu( HMENU hMenu )
4067 LPPOPUPMENU lppop;
4069 TRACE("(%p)\n", hMenu);
4071 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4072 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4074 /* DestroyMenu should not destroy system menu popup owner */
4075 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4077 DestroyWindow( lppop->hWnd );
4078 lppop->hWnd = 0;
4081 if (lppop->items) /* recursively destroy submenus */
4083 int i;
4084 MENUITEM *item = lppop->items;
4085 for (i = lppop->nItems; i > 0; i--, item++)
4087 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4088 MENU_FreeItemData( item );
4090 HeapFree( GetProcessHeap(), 0, lppop->items );
4092 HeapFree( GetProcessHeap(), 0, lppop );
4093 return TRUE;
4097 /**********************************************************************
4098 * GetSystemMenu (USER32.@)
4100 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4102 WND *wndPtr = WIN_GetPtr( hWnd );
4103 HMENU retvalue = 0;
4105 if (wndPtr == WND_DESKTOP) return 0;
4106 if (wndPtr == WND_OTHER_PROCESS)
4108 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4110 else if (wndPtr)
4112 if (wndPtr->hSysMenu && bRevert)
4114 DestroyMenu(wndPtr->hSysMenu);
4115 wndPtr->hSysMenu = 0;
4118 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4119 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4121 if( wndPtr->hSysMenu )
4123 POPUPMENU *menu;
4124 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4126 /* Store the dummy sysmenu handle to facilitate the refresh */
4127 /* of the close button if the SC_CLOSE item change */
4128 menu = MENU_GetMenu(retvalue);
4129 if ( menu )
4130 menu->hSysMenuOwner = wndPtr->hSysMenu;
4132 WIN_ReleasePtr( wndPtr );
4134 return bRevert ? 0 : retvalue;
4138 /*******************************************************************
4139 * SetSystemMenu (USER32.@)
4141 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4143 WND *wndPtr = WIN_GetPtr( hwnd );
4145 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4147 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4148 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4149 WIN_ReleasePtr( wndPtr );
4150 return TRUE;
4152 return FALSE;
4156 /**********************************************************************
4157 * GetMenu (USER32.@)
4159 HMENU WINAPI GetMenu( HWND hWnd )
4161 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4162 TRACE("for %p returning %p\n", hWnd, retvalue);
4163 return retvalue;
4166 /**********************************************************************
4167 * GetMenuBarInfo (USER32.@)
4169 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4171 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4172 return FALSE;
4175 /**********************************************************************
4176 * MENU_SetMenu
4178 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4179 * SetWindowPos call that would result if SetMenu were called directly.
4181 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4183 TRACE("(%p, %p);\n", hWnd, hMenu);
4185 if (hMenu && !IsMenu(hMenu))
4187 WARN("hMenu %p is not a menu handle\n", hMenu);
4188 return FALSE;
4190 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4191 return FALSE;
4193 hWnd = WIN_GetFullHandle( hWnd );
4194 if (GetCapture() == hWnd)
4195 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4197 if (hMenu != 0)
4199 LPPOPUPMENU lpmenu;
4201 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4203 lpmenu->hWnd = hWnd;
4204 lpmenu->Height = 0; /* Make sure we recalculate the size */
4206 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4207 return TRUE;
4211 /**********************************************************************
4212 * SetMenu (USER32.@)
4214 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4216 if(!MENU_SetMenu(hWnd, hMenu))
4217 return FALSE;
4219 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4220 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4221 return TRUE;
4225 /**********************************************************************
4226 * GetSubMenu (USER32.@)
4228 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4230 MENUITEM * lpmi;
4232 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4233 if (!(lpmi->fType & MF_POPUP)) return 0;
4234 return lpmi->hSubMenu;
4238 /**********************************************************************
4239 * DrawMenuBar (USER32.@)
4241 BOOL WINAPI DrawMenuBar( HWND hWnd )
4243 LPPOPUPMENU lppop;
4244 HMENU hMenu = GetMenu(hWnd);
4246 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4247 return FALSE;
4248 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4250 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4251 lppop->hwndOwner = hWnd;
4252 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4253 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4254 return TRUE;
4257 /***********************************************************************
4258 * DrawMenuBarTemp (USER32.@)
4260 * UNDOCUMENTED !!
4262 * called by W98SE desk.cpl Control Panel Applet
4264 * Not 100% sure about the param names, but close.
4266 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4268 LPPOPUPMENU lppop;
4269 UINT i,retvalue;
4270 HFONT hfontOld = 0;
4271 BOOL flat_menu = FALSE;
4273 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4275 if (!hMenu)
4276 hMenu = GetMenu(hwnd);
4278 if (!hFont)
4279 hFont = get_menu_font(FALSE);
4281 lppop = MENU_GetMenu( hMenu );
4282 if (lppop == NULL || lprect == NULL)
4284 retvalue = GetSystemMetrics(SM_CYMENU);
4285 goto END;
4288 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4290 hfontOld = SelectObject( hDC, hFont);
4292 if (lppop->Height == 0)
4293 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4295 lprect->bottom = lprect->top + lppop->Height;
4297 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4299 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4300 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4301 LineTo( hDC, lprect->right, lprect->bottom );
4303 if (lppop->nItems == 0)
4305 retvalue = GetSystemMetrics(SM_CYMENU);
4306 goto END;
4309 for (i = 0; i < lppop->nItems; i++)
4311 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4312 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4314 retvalue = lppop->Height;
4316 END:
4317 if (hfontOld) SelectObject (hDC, hfontOld);
4318 return retvalue;
4321 /***********************************************************************
4322 * EndMenu (USER.187)
4323 * EndMenu (USER32.@)
4325 BOOL WINAPI EndMenu(void)
4327 /* if we are in the menu code, and it is active */
4328 if (!fEndMenu && top_popup)
4330 /* terminate the menu handling code */
4331 fEndMenu = TRUE;
4333 /* needs to be posted to wakeup the internal menu handler */
4334 /* which will now terminate the menu, in the event that */
4335 /* the main window was minimized, or lost focus, so we */
4336 /* don't end up with an orphaned menu */
4337 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4339 return fEndMenu;
4343 /*****************************************************************
4344 * LoadMenuA (USER32.@)
4346 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4348 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4349 if (!hrsrc) return 0;
4350 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4354 /*****************************************************************
4355 * LoadMenuW (USER32.@)
4357 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4359 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4360 if (!hrsrc) return 0;
4361 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4365 /**********************************************************************
4366 * LoadMenuIndirectW (USER32.@)
4368 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4370 HMENU hMenu;
4371 WORD version, offset;
4372 LPCSTR p = template;
4374 version = GET_WORD(p);
4375 p += sizeof(WORD);
4376 TRACE("%p, ver %d\n", template, version );
4377 switch (version)
4379 case 0: /* standard format is version of 0 */
4380 offset = GET_WORD(p);
4381 p += sizeof(WORD) + offset;
4382 if (!(hMenu = CreateMenu())) return 0;
4383 if (!MENU_ParseResource( p, hMenu ))
4385 DestroyMenu( hMenu );
4386 return 0;
4388 return hMenu;
4389 case 1: /* extended format is version of 1 */
4390 offset = GET_WORD(p);
4391 p += sizeof(WORD) + offset;
4392 if (!(hMenu = CreateMenu())) return 0;
4393 if (!MENUEX_ParseResource( p, hMenu))
4395 DestroyMenu( hMenu );
4396 return 0;
4398 return hMenu;
4399 default:
4400 ERR("version %d not supported.\n", version);
4401 return 0;
4406 /**********************************************************************
4407 * LoadMenuIndirectA (USER32.@)
4409 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4411 return LoadMenuIndirectW( template );
4415 /**********************************************************************
4416 * IsMenu (USER32.@)
4418 BOOL WINAPI IsMenu(HMENU hmenu)
4420 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4422 if (!menu)
4424 SetLastError(ERROR_INVALID_MENU_HANDLE);
4425 return FALSE;
4427 return TRUE;
4430 /**********************************************************************
4431 * GetMenuItemInfo_common
4434 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4435 LPMENUITEMINFOW lpmii, BOOL unicode)
4437 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4439 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4441 if (!menu) {
4442 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4443 return FALSE;
4446 if( lpmii->fMask & MIIM_TYPE) {
4447 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4448 WARN("invalid combination of fMask bits used\n");
4449 /* this does not happen on Win9x/ME */
4450 SetLastError( ERROR_INVALID_PARAMETER);
4451 return FALSE;
4453 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4454 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4455 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4456 if( lpmii->fType & MFT_BITMAP) {
4457 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4458 lpmii->cch = 0;
4459 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4460 /* this does not happen on Win9x/ME */
4461 lpmii->dwTypeData = 0;
4462 lpmii->cch = 0;
4466 /* copy the text string */
4467 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4468 if( !menu->text ) {
4469 if(lpmii->dwTypeData && lpmii->cch) {
4470 lpmii->cch = 0;
4471 if( unicode)
4472 *((WCHAR *)lpmii->dwTypeData) = 0;
4473 else
4474 *((CHAR *)lpmii->dwTypeData) = 0;
4476 } else {
4477 int len;
4478 if (unicode)
4480 len = strlenW(menu->text);
4481 if(lpmii->dwTypeData && lpmii->cch)
4482 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4484 else
4486 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4487 0, NULL, NULL ) - 1;
4488 if(lpmii->dwTypeData && lpmii->cch)
4489 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4490 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4491 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4493 /* if we've copied a substring we return its length */
4494 if(lpmii->dwTypeData && lpmii->cch)
4495 if (lpmii->cch <= len + 1)
4496 lpmii->cch--;
4497 else
4498 lpmii->cch = len;
4499 else {
4500 /* return length of string */
4501 /* not on Win9x/ME if fType & MFT_BITMAP */
4502 lpmii->cch = len;
4507 if (lpmii->fMask & MIIM_FTYPE)
4508 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4510 if (lpmii->fMask & MIIM_BITMAP)
4511 lpmii->hbmpItem = menu->hbmpItem;
4513 if (lpmii->fMask & MIIM_STATE)
4514 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4516 if (lpmii->fMask & MIIM_ID)
4517 lpmii->wID = menu->wID;
4519 if (lpmii->fMask & MIIM_SUBMENU)
4520 lpmii->hSubMenu = menu->hSubMenu;
4521 else {
4522 /* hSubMenu is always cleared
4523 * (not on Win9x/ME ) */
4524 lpmii->hSubMenu = 0;
4527 if (lpmii->fMask & MIIM_CHECKMARKS) {
4528 lpmii->hbmpChecked = menu->hCheckBit;
4529 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4531 if (lpmii->fMask & MIIM_DATA)
4532 lpmii->dwItemData = menu->dwItemData;
4534 return TRUE;
4537 /**********************************************************************
4538 * GetMenuItemInfoA (USER32.@)
4540 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4541 LPMENUITEMINFOA lpmii)
4543 BOOL ret;
4544 MENUITEMINFOA mii;
4545 if( lpmii->cbSize != sizeof( mii) &&
4546 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4547 SetLastError( ERROR_INVALID_PARAMETER);
4548 return FALSE;
4550 memcpy( &mii, lpmii, lpmii->cbSize);
4551 mii.cbSize = sizeof( mii);
4552 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4553 (LPMENUITEMINFOW)&mii, FALSE);
4554 mii.cbSize = lpmii->cbSize;
4555 memcpy( lpmii, &mii, mii.cbSize);
4556 return ret;
4559 /**********************************************************************
4560 * GetMenuItemInfoW (USER32.@)
4562 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4563 LPMENUITEMINFOW lpmii)
4565 BOOL ret;
4566 MENUITEMINFOW mii;
4567 if( lpmii->cbSize != sizeof( mii) &&
4568 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4569 SetLastError( ERROR_INVALID_PARAMETER);
4570 return FALSE;
4572 memcpy( &mii, lpmii, lpmii->cbSize);
4573 mii.cbSize = sizeof( mii);
4574 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4575 mii.cbSize = lpmii->cbSize;
4576 memcpy( lpmii, &mii, mii.cbSize);
4577 return ret;
4581 /* set a menu item text from a ASCII or Unicode string */
4582 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4584 if (!text)
4585 menu->text = NULL;
4586 else if (unicode)
4588 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4589 strcpyW( menu->text, text );
4591 else
4593 LPCSTR str = (LPCSTR)text;
4594 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4595 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4596 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4601 /**********************************************************************
4602 * MENU_depth
4604 * detect if there are loops in the menu tree (or the depth is too large)
4606 static int MENU_depth( POPUPMENU *pmenu, int depth)
4608 int i;
4609 MENUITEM *item;
4610 int subdepth;
4612 depth++;
4613 if( depth > MAXMENUDEPTH) return depth;
4614 item = pmenu->items;
4615 subdepth = depth;
4616 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4617 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4618 if( psubmenu){
4619 int bdepth = MENU_depth( psubmenu, depth);
4620 if( bdepth > subdepth) subdepth = bdepth;
4622 if( subdepth > MAXMENUDEPTH)
4623 TRACE("<- hmenu %p\n", item->hSubMenu);
4625 return subdepth;
4629 /**********************************************************************
4630 * SetMenuItemInfo_common
4632 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4633 * MIIM_BITMAP and MIIM_STRING flags instead.
4636 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4637 const MENUITEMINFOW *lpmii,
4638 BOOL unicode)
4640 if (!menu) return FALSE;
4642 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4644 if (lpmii->fMask & MIIM_FTYPE ) {
4645 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4646 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4648 if (lpmii->fMask & MIIM_STRING ) {
4649 /* free the string when used */
4650 HeapFree(GetProcessHeap(), 0, menu->text);
4651 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4654 if (lpmii->fMask & MIIM_STATE)
4655 /* Other menu items having MFS_DEFAULT are not converted
4656 to normal items */
4657 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4659 if (lpmii->fMask & MIIM_ID)
4660 menu->wID = lpmii->wID;
4662 if (lpmii->fMask & MIIM_SUBMENU) {
4663 menu->hSubMenu = lpmii->hSubMenu;
4664 if (menu->hSubMenu) {
4665 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4666 if (subMenu) {
4667 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4668 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4669 menu->hSubMenu = 0;
4670 return FALSE;
4672 subMenu->wFlags |= MF_POPUP;
4673 menu->fType |= MF_POPUP;
4674 } else {
4675 SetLastError( ERROR_INVALID_PARAMETER);
4676 return FALSE;
4679 else
4680 menu->fType &= ~MF_POPUP;
4683 if (lpmii->fMask & MIIM_CHECKMARKS)
4685 menu->hCheckBit = lpmii->hbmpChecked;
4686 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4688 if (lpmii->fMask & MIIM_DATA)
4689 menu->dwItemData = lpmii->dwItemData;
4691 if (lpmii->fMask & MIIM_BITMAP)
4692 menu->hbmpItem = lpmii->hbmpItem;
4694 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4695 menu->fType |= MFT_SEPARATOR;
4697 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4698 return TRUE;
4701 /**********************************************************************
4702 * MENU_NormalizeMenuItemInfoStruct
4704 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4705 * check, copy and extend the MENUITEMINFO struct from the version that the application
4706 * supplied to the version used by wine source. */
4707 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4708 MENUITEMINFOW *pmii_out )
4710 /* do we recognize the size? */
4711 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4712 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4713 SetLastError( ERROR_INVALID_PARAMETER);
4714 return FALSE;
4716 /* copy the fields that we have */
4717 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4718 /* if the hbmpItem member is missing then extend */
4719 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4720 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4721 pmii_out->hbmpItem = NULL;
4723 /* test for invalid bit combinations */
4724 if( (pmii_out->fMask & MIIM_TYPE &&
4725 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4726 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4727 WARN("invalid combination of fMask bits used\n");
4728 /* this does not happen on Win9x/ME */
4729 SetLastError( ERROR_INVALID_PARAMETER);
4730 return FALSE;
4732 /* convert old style (MIIM_TYPE) to the new */
4733 if( pmii_out->fMask & MIIM_TYPE){
4734 pmii_out->fMask |= MIIM_FTYPE;
4735 if( IS_STRING_ITEM(pmii_out->fType)){
4736 pmii_out->fMask |= MIIM_STRING;
4737 } else if( (pmii_out->fType) & MFT_BITMAP){
4738 pmii_out->fMask |= MIIM_BITMAP;
4739 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4742 return TRUE;
4745 /**********************************************************************
4746 * SetMenuItemInfoA (USER32.@)
4748 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4749 const MENUITEMINFOA *lpmii)
4751 MENUITEMINFOW mii;
4753 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4755 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4757 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4758 &mii, FALSE);
4761 /**********************************************************************
4762 * SetMenuItemInfoW (USER32.@)
4764 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4765 const MENUITEMINFOW *lpmii)
4767 MENUITEMINFOW mii;
4769 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4771 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4772 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4773 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4776 /**********************************************************************
4777 * SetMenuDefaultItem (USER32.@)
4780 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4782 UINT i;
4783 POPUPMENU *menu;
4784 MENUITEM *item;
4786 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4788 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4790 /* reset all default-item flags */
4791 item = menu->items;
4792 for (i = 0; i < menu->nItems; i++, item++)
4794 item->fState &= ~MFS_DEFAULT;
4797 /* no default item */
4798 if ( -1 == uItem)
4800 return TRUE;
4803 item = menu->items;
4804 if ( bypos )
4806 if ( uItem >= menu->nItems ) return FALSE;
4807 item[uItem].fState |= MFS_DEFAULT;
4808 return TRUE;
4810 else
4812 for (i = 0; i < menu->nItems; i++, item++)
4814 if (item->wID == uItem)
4816 item->fState |= MFS_DEFAULT;
4817 return TRUE;
4822 return FALSE;
4825 /**********************************************************************
4826 * GetMenuDefaultItem (USER32.@)
4828 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4830 POPUPMENU *menu;
4831 MENUITEM * item;
4832 UINT i = 0;
4834 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4836 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4838 /* find default item */
4839 item = menu->items;
4841 /* empty menu */
4842 if (! item) return -1;
4844 while ( !( item->fState & MFS_DEFAULT ) )
4846 i++; item++;
4847 if (i >= menu->nItems ) return -1;
4850 /* default: don't return disabled items */
4851 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4853 /* search rekursiv when needed */
4854 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4856 UINT ret;
4857 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4858 if ( -1 != ret ) return ret;
4860 /* when item not found in submenu, return the popup item */
4862 return ( bypos ) ? i : item->wID;
4867 /**********************************************************************
4868 * InsertMenuItemA (USER32.@)
4870 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4871 const MENUITEMINFOA *lpmii)
4873 MENUITEM *item;
4874 MENUITEMINFOW mii;
4876 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4878 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4880 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4881 return SetMenuItemInfo_common(item, &mii, FALSE);
4885 /**********************************************************************
4886 * InsertMenuItemW (USER32.@)
4888 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4889 const MENUITEMINFOW *lpmii)
4891 MENUITEM *item;
4892 MENUITEMINFOW mii;
4894 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4896 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4898 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4899 return SetMenuItemInfo_common(item, &mii, TRUE);
4902 /**********************************************************************
4903 * CheckMenuRadioItem (USER32.@)
4906 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4907 UINT first, UINT last, UINT check,
4908 UINT bypos)
4910 BOOL done = FALSE;
4911 UINT i;
4912 MENUITEM *mi_first = NULL, *mi_check;
4913 HMENU m_first, m_check;
4915 for (i = first; i <= last; i++)
4917 UINT pos = i;
4919 if (!mi_first)
4921 m_first = hMenu;
4922 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4923 if (!mi_first) continue;
4924 mi_check = mi_first;
4925 m_check = m_first;
4927 else
4929 m_check = hMenu;
4930 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4931 if (!mi_check) continue;
4934 if (m_first != m_check) continue;
4935 if (mi_check->fType == MFT_SEPARATOR) continue;
4937 if (i == check)
4939 mi_check->fType |= MFT_RADIOCHECK;
4940 mi_check->fState |= MFS_CHECKED;
4941 done = TRUE;
4943 else
4945 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4946 mi_check->fState &= ~MFS_CHECKED;
4950 return done;
4954 /**********************************************************************
4955 * GetMenuItemRect (USER32.@)
4957 * ATTENTION: Here, the returned values in rect are the screen
4958 * coordinates of the item just like if the menu was
4959 * always on the upper left side of the application.
4962 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4963 LPRECT rect)
4965 POPUPMENU *itemMenu;
4966 MENUITEM *item;
4967 HWND referenceHwnd;
4969 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4971 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4972 referenceHwnd = hwnd;
4974 if(!hwnd)
4976 itemMenu = MENU_GetMenu(hMenu);
4977 if (itemMenu == NULL)
4978 return FALSE;
4980 if(itemMenu->hWnd == 0)
4981 return FALSE;
4982 referenceHwnd = itemMenu->hWnd;
4985 if ((rect == NULL) || (item == NULL))
4986 return FALSE;
4988 *rect = item->rect;
4990 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4992 return TRUE;
4995 /**********************************************************************
4996 * SetMenuInfo (USER32.@)
4998 * FIXME
4999 * actually use the items to draw the menu
5000 * (recalculate and/or redraw)
5002 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5004 POPUPMENU *menu;
5005 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5007 if (lpmi->fMask & MIM_BACKGROUND)
5008 menu->hbrBack = lpmi->hbrBack;
5010 if (lpmi->fMask & MIM_HELPID)
5011 menu->dwContextHelpID = lpmi->dwContextHelpID;
5013 if (lpmi->fMask & MIM_MAXHEIGHT)
5014 menu->cyMax = lpmi->cyMax;
5016 if (lpmi->fMask & MIM_MENUDATA)
5017 menu->dwMenuData = lpmi->dwMenuData;
5019 if (lpmi->fMask & MIM_STYLE)
5020 menu->dwStyle = lpmi->dwStyle;
5022 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5023 int i;
5024 MENUITEM *item = menu->items;
5025 for( i = menu->nItems; i; i--, item++)
5026 if( item->fType & MF_POPUP)
5027 menu_SetMenuInfo( item->hSubMenu, lpmi);
5029 return TRUE;
5032 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5034 TRACE("(%p %p)\n", hMenu, lpmi);
5035 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5036 if( lpmi->fMask & MIM_STYLE) {
5037 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5038 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5039 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5041 return TRUE;
5043 SetLastError( ERROR_INVALID_PARAMETER);
5044 return FALSE;
5047 /**********************************************************************
5048 * GetMenuInfo (USER32.@)
5050 * NOTES
5051 * win98/NT5.0
5054 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5055 { POPUPMENU *menu;
5057 TRACE("(%p %p)\n", hMenu, lpmi);
5059 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5062 if (lpmi->fMask & MIM_BACKGROUND)
5063 lpmi->hbrBack = menu->hbrBack;
5065 if (lpmi->fMask & MIM_HELPID)
5066 lpmi->dwContextHelpID = menu->dwContextHelpID;
5068 if (lpmi->fMask & MIM_MAXHEIGHT)
5069 lpmi->cyMax = menu->cyMax;
5071 if (lpmi->fMask & MIM_MENUDATA)
5072 lpmi->dwMenuData = menu->dwMenuData;
5074 if (lpmi->fMask & MIM_STYLE)
5075 lpmi->dwStyle = menu->dwStyle;
5077 return TRUE;
5079 SetLastError( ERROR_INVALID_PARAMETER);
5080 return FALSE;
5084 /**********************************************************************
5085 * SetMenuContextHelpId (USER32.@)
5087 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5089 LPPOPUPMENU menu;
5091 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5093 if ((menu = MENU_GetMenu(hMenu)))
5095 menu->dwContextHelpID = dwContextHelpID;
5096 return TRUE;
5098 return FALSE;
5102 /**********************************************************************
5103 * GetMenuContextHelpId (USER32.@)
5105 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5107 LPPOPUPMENU menu;
5109 TRACE("(%p)\n", hMenu);
5111 if ((menu = MENU_GetMenu(hMenu)))
5113 return menu->dwContextHelpID;
5115 return 0;
5118 /**********************************************************************
5119 * MenuItemFromPoint (USER32.@)
5121 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5123 POPUPMENU *menu = MENU_GetMenu(hMenu);
5124 UINT pos;
5126 /*FIXME: Do we have to handle hWnd here? */
5127 if (!menu) return -1;
5128 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5129 return pos;
5133 /**********************************************************************
5134 * translate_accelerator
5136 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5137 BYTE fVirt, WORD key, WORD cmd )
5139 INT mask = 0;
5140 UINT mesg = 0;
5142 if (wParam != key) return FALSE;
5144 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5145 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5146 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5148 if (message == WM_CHAR || message == WM_SYSCHAR)
5150 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5152 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5153 goto found;
5156 else
5158 if(fVirt & FVIRTKEY)
5160 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5161 wParam, 0xff & HIWORD(lParam));
5163 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5164 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5166 else
5168 if (!(lParam & 0x01000000)) /* no special_key */
5170 if ((fVirt & FALT) && (lParam & 0x20000000))
5171 { /* ^^ ALT pressed */
5172 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5173 goto found;
5178 return FALSE;
5180 found:
5181 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5182 mesg = 1;
5183 else
5185 HMENU hMenu, hSubMenu, hSysMenu;
5186 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5188 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5189 hSysMenu = get_win_sys_menu( hWnd );
5191 /* find menu item and ask application to initialize it */
5192 /* 1. in the system menu */
5193 hSubMenu = hSysMenu;
5194 nPos = cmd;
5195 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5197 if (GetCapture())
5198 mesg = 2;
5199 if (!IsWindowEnabled(hWnd))
5200 mesg = 3;
5201 else
5203 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5204 if(hSubMenu != hSysMenu)
5206 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5207 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5208 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5210 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5213 else /* 2. in the window's menu */
5215 hSubMenu = hMenu;
5216 nPos = cmd;
5217 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5219 if (GetCapture())
5220 mesg = 2;
5221 if (!IsWindowEnabled(hWnd))
5222 mesg = 3;
5223 else
5225 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5226 if(hSubMenu != hMenu)
5228 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5229 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5230 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5232 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5237 if (mesg == 0)
5239 if (uSysStat != (UINT)-1)
5241 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5242 mesg=4;
5243 else
5244 mesg=WM_SYSCOMMAND;
5246 else
5248 if (uStat != (UINT)-1)
5250 if (IsIconic(hWnd))
5251 mesg=5;
5252 else
5254 if (uStat & (MF_DISABLED|MF_GRAYED))
5255 mesg=6;
5256 else
5257 mesg=WM_COMMAND;
5260 else
5261 mesg=WM_COMMAND;
5266 if( mesg==WM_COMMAND )
5268 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5269 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5271 else if( mesg==WM_SYSCOMMAND )
5273 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5274 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5276 else
5278 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5279 * #0: unknown (please report!)
5280 * #1: for WM_KEYUP,WM_SYSKEYUP
5281 * #2: mouse is captured
5282 * #3: window is disabled
5283 * #4: it's a disabled system menu option
5284 * #5: it's a menu option, but window is iconic
5285 * #6: it's a menu option, but disabled
5287 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5288 if(mesg==0)
5289 ERR_(accel)(" unknown reason - please report!\n");
5291 return TRUE;
5294 /**********************************************************************
5295 * TranslateAcceleratorA (USER32.@)
5296 * TranslateAccelerator (USER32.@)
5298 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5300 switch (msg->message)
5302 case WM_KEYDOWN:
5303 case WM_SYSKEYDOWN:
5304 return TranslateAcceleratorW( hWnd, hAccel, msg );
5306 case WM_CHAR:
5307 case WM_SYSCHAR:
5309 MSG msgW = *msg;
5310 char ch = LOWORD(msg->wParam);
5311 WCHAR wch;
5312 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5313 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5314 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5317 default:
5318 return 0;
5322 /**********************************************************************
5323 * TranslateAcceleratorW (USER32.@)
5325 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5327 ACCEL data[32], *ptr = data;
5328 int i, count;
5330 if (!hWnd) return 0;
5332 if (msg->message != WM_KEYDOWN &&
5333 msg->message != WM_SYSKEYDOWN &&
5334 msg->message != WM_CHAR &&
5335 msg->message != WM_SYSCHAR)
5336 return 0;
5338 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5339 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5341 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5342 if (count > sizeof(data)/sizeof(data[0]))
5344 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5346 count = CopyAcceleratorTableW( hAccel, ptr, count );
5347 for (i = 0; i < count; i++)
5349 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5350 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5351 break;
5353 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5354 return (i < count);