gdi32: The text extents functions fail if passed a negative count.
[wine.git] / dlls / user32 / menu.c
blob3ec682fe56fdcce3b0ad10334ca5262ebcf4b0ee
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(BOOL mdi)
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
426 if( hMenu ) {
427 MENUINFO minfo;
428 MENUITEMINFOW miteminfo;
429 POPUPMENU* menu = MENU_GetMenu(hMenu);
430 menu->wFlags |= MF_SYSMENU | MF_POPUP;
431 /* decorate the menu with bitmaps */
432 minfo.cbSize = sizeof( MENUINFO);
433 minfo.dwStyle = MNS_CHECKORBMP;
434 minfo.fMask = MIM_STYLE;
435 SetMenuInfo( hMenu, &minfo);
436 miteminfo.cbSize = sizeof( MENUITEMINFOW);
437 miteminfo.fMask = MIIM_BITMAP;
438 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
439 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
440 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
441 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
443 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
446 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
448 else
449 ERR("Unable to load default system menu\n" );
451 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
453 return hMenu;
457 /**********************************************************************
458 * MENU_GetSysMenu
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
466 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
468 HMENU hMenu;
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
471 if ((hMenu = CreateMenu()))
473 POPUPMENU *menu = MENU_GetMenu(hMenu);
474 menu->wFlags = MF_SYSMENU;
475 menu->hWnd = WIN_GetFullHandle( hWnd );
476 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
478 if (!hPopupMenu)
480 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
481 hPopupMenu = MENU_CopySysPopup(TRUE);
482 else
483 hPopupMenu = MENU_CopySysPopup(FALSE);
486 if (hPopupMenu)
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
499 return hMenu;
501 DestroyMenu( hMenu );
503 ERR("failed to load system menu!\n");
504 return 0;
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
511 * Grey the appropriate items in System menu.
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
515 BOOL gray;
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
529 /* The menu item must keep its state if it's disabled */
530 if(gray)
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
535 /******************************************************************************
537 * UINT MENU_GetStartOfNextColumn(
538 * HMENU hMenu )
540 *****************************************************************************/
542 static UINT MENU_GetStartOfNextColumn(
543 HMENU hMenu )
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 UINT i;
548 if(!menu)
549 return NO_SELECTED_ITEM;
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
553 return i;
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return i;
560 return NO_SELECTED_ITEM;
564 /******************************************************************************
566 * UINT MENU_GetStartOfPrevColumn(
567 * HMENU hMenu )
569 *****************************************************************************/
571 static UINT MENU_GetStartOfPrevColumn(
572 HMENU hMenu )
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 UINT i;
577 if( !menu )
578 return NO_SELECTED_ITEM;
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
583 /* Find the start of the column */
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 --i); /* empty */
589 if(i == 0)
590 return NO_SELECTED_ITEM;
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 break;
597 TRACE("ret %d.\n", i );
599 return i;
604 /***********************************************************************
605 * MENU_FindItem
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
612 POPUPMENU *menu;
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
615 UINT i;
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
623 else
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
628 if (item->fType & MF_POPUP)
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
632 if (subitem)
634 *hmenu = hsubmenu;
635 return subitem;
637 else if (item->wID == *nPos)
639 /* fallback to this item if nothing else found */
640 fallback_pos = i;
641 fallback = item;
644 else if (item->wID == *nPos)
646 *nPos = i;
647 return item;
652 if (fallback)
653 *nPos = fallback_pos;
655 return fallback;
658 /***********************************************************************
659 * MENU_FindSubMenu
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
665 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
667 POPUPMENU *menu;
668 UINT i;
669 MENUITEM *item;
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
673 item = menu->items;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
677 return i;
679 else {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
683 *hmenu = hsubmenu;
684 return pos;
688 return NO_SELECTED_ITEM;
691 /***********************************************************************
692 * MENU_FreeItemData
694 static void MENU_FreeItemData( MENUITEM* item )
696 /* delete text */
697 HeapFree( GetProcessHeap(), 0, item->text );
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
703 * Adjust menu item rectangle according to scrolling state.
705 static void
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
708 if (menu->bScrolling)
710 UINT arrow_bitmap_height;
711 BITMAP bmp;
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
721 /***********************************************************************
722 * MENU_FindItemByCoords
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
731 MENUITEM *item;
732 UINT i;
733 RECT rect;
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
737 else pt.x -= rect.left;
738 pt.y -= rect.top;
739 item = menu->items;
740 for (i = 0; i < menu->nItems; i++, item++)
742 rect = item->rect;
743 MENU_AdjustMenuItemRect(menu, &rect);
744 if (PtInRect(&rect, pt))
746 if (pos) *pos = i;
747 return item;
750 return NULL;
754 /***********************************************************************
755 * MENU_FindItemByKey
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
760 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
761 WCHAR key, BOOL forceMenuChar )
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
765 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
767 if (hmenu)
769 POPUPMENU *menu = MENU_GetMenu( hmenu );
770 MENUITEM *item = menu->items;
771 LRESULT menuchar;
773 if( !forceMenuChar )
775 UINT i;
777 for (i = 0; i < menu->nItems; i++, item++)
779 if( item->text)
781 WCHAR *p = item->text - 2;
784 p = strchrW (p + 2, '&');
786 while (p != NULL && p [1] == '&');
787 if (p && (toupperW(p[1]) == toupperW(key))) return i;
791 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
792 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
793 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
794 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
796 return (UINT)(-1);
800 /***********************************************************************
801 * MENU_GetBitmapItemSize
803 * Get the size of a bitmap item.
805 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
806 HWND hwndOwner)
808 BITMAP bm;
809 HBITMAP bmp = lpitem->hbmpItem;
811 size->cx = size->cy = 0;
813 /* check if there is a magic menu item associated with this item */
814 switch( (INT_PTR) bmp )
816 case (INT_PTR)HBMMENU_CALLBACK:
818 MEASUREITEMSTRUCT measItem;
819 measItem.CtlType = ODT_MENU;
820 measItem.CtlID = 0;
821 measItem.itemID = lpitem->wID;
822 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
823 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
824 measItem.itemData = lpitem->dwItemData;
825 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
826 size->cx = measItem.itemWidth;
827 size->cy = measItem.itemHeight;
828 return;
830 break;
831 case (INT_PTR)HBMMENU_SYSTEM:
832 if (lpitem->dwItemData)
834 bmp = (HBITMAP)lpitem->dwItemData;
835 break;
837 /* fall through */
838 case (INT_PTR)HBMMENU_MBAR_RESTORE:
839 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
840 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
841 case (INT_PTR)HBMMENU_MBAR_CLOSE:
842 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
843 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
844 size->cy = size->cx;
845 return;
846 case (INT_PTR)HBMMENU_POPUP_CLOSE:
847 case (INT_PTR)HBMMENU_POPUP_RESTORE:
848 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
849 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
850 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
851 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
852 return;
854 if (GetObjectW(bmp, sizeof(bm), &bm ))
856 size->cx = bm.bmWidth;
857 size->cy = bm.bmHeight;
861 /***********************************************************************
862 * MENU_DrawBitmapItem
864 * Draw a bitmap item.
866 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
867 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
869 BITMAP bm;
870 DWORD rop;
871 HDC hdcMem;
872 HBITMAP bmp;
873 int w = rect->right - rect->left;
874 int h = rect->bottom - rect->top;
875 int bmp_xoffset = 0;
876 int left, top;
877 HBITMAP hbmToDraw = lpitem->hbmpItem;
878 bmp = hbmToDraw;
880 /* Check if there is a magic menu item associated with this item */
881 if (IS_MAGIC_BITMAP(hbmToDraw))
883 UINT flags = 0;
884 WCHAR bmchr = 0;
885 RECT r;
887 switch((INT_PTR)hbmToDraw)
889 case (INT_PTR)HBMMENU_SYSTEM:
890 if (lpitem->dwItemData)
892 bmp = (HBITMAP)lpitem->dwItemData;
893 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
895 else
897 static HBITMAP hBmpSysMenu;
899 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
900 bmp = hBmpSysMenu;
901 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
902 /* only use right half of the bitmap */
903 bmp_xoffset = bm.bmWidth / 2;
904 bm.bmWidth -= bmp_xoffset;
906 goto got_bitmap;
907 case (INT_PTR)HBMMENU_MBAR_RESTORE:
908 flags = DFCS_CAPTIONRESTORE;
909 break;
910 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
911 flags = DFCS_CAPTIONMIN;
912 break;
913 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
914 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
915 break;
916 case (INT_PTR)HBMMENU_MBAR_CLOSE:
917 flags = DFCS_CAPTIONCLOSE;
918 break;
919 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
920 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
921 break;
922 case (INT_PTR)HBMMENU_CALLBACK:
924 DRAWITEMSTRUCT drawItem;
925 drawItem.CtlType = ODT_MENU;
926 drawItem.CtlID = 0;
927 drawItem.itemID = lpitem->wID;
928 drawItem.itemAction = odaction;
929 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
930 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
931 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
932 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
933 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
934 drawItem.hwndItem = (HWND)hmenu;
935 drawItem.hDC = hdc;
936 drawItem.itemData = lpitem->dwItemData;
937 drawItem.rcItem = *rect;
938 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
939 return;
941 break;
942 case (INT_PTR)HBMMENU_POPUP_CLOSE:
943 bmchr = 0x72;
944 break;
945 case (INT_PTR)HBMMENU_POPUP_RESTORE:
946 bmchr = 0x32;
947 break;
948 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
949 bmchr = 0x31;
950 break;
951 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
952 bmchr = 0x30;
953 break;
954 default:
955 FIXME("Magic %p not implemented\n", hbmToDraw);
956 return;
958 if (bmchr)
960 /* draw the magic bitmaps using marlett font characters */
961 /* FIXME: fontsize and the position (x,y) could probably be better */
962 HFONT hfont, hfontsav;
963 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
964 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
965 { 'M','a','r','l','e','t','t',0 } };
966 logfont.lfHeight = min( h, w) - 5 ;
967 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
968 hfont = CreateFontIndirectW( &logfont);
969 hfontsav = SelectObject(hdc, hfont);
970 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
971 SelectObject(hdc, hfontsav);
972 DeleteObject( hfont);
974 else
976 r = *rect;
977 InflateRect( &r, -1, -1 );
978 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
979 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
981 return;
984 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
986 got_bitmap:
987 hdcMem = CreateCompatibleDC( hdc );
988 SelectObject( hdcMem, bmp );
990 /* handle fontsize > bitmap_height */
991 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
992 left=rect->left;
993 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
994 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
995 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
996 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
997 DeleteDC( hdcMem );
1001 /***********************************************************************
1002 * MENU_CalcItemSize
1004 * Calculate the size of the menu item and store it in lpitem->rect.
1006 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1007 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1009 WCHAR *p;
1010 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1011 UINT arrow_bitmap_width;
1012 BITMAP bm;
1013 INT itemheight;
1015 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1016 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1017 (menuBar ? " (MenuBar)" : ""));
1019 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1020 arrow_bitmap_width = bm.bmWidth;
1022 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1023 if( !menucharsize.cx ) {
1024 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1025 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1026 * but it is unlikely an application will depend on that */
1027 ODitemheight = HIWORD( GetDialogBaseUnits());
1030 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1032 if (lpitem->fType & MF_OWNERDRAW)
1034 MEASUREITEMSTRUCT mis;
1035 mis.CtlType = ODT_MENU;
1036 mis.CtlID = 0;
1037 mis.itemID = lpitem->wID;
1038 mis.itemData = lpitem->dwItemData;
1039 mis.itemHeight = ODitemheight;
1040 mis.itemWidth = 0;
1041 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1042 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1043 * width of a menufont character to the width of an owner-drawn menu.
1045 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1046 if (menuBar) {
1047 /* under at least win95 you seem to be given a standard
1048 height for the menu and the height value is ignored */
1049 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1050 } else
1051 lpitem->rect.bottom += mis.itemHeight;
1053 TRACE("id=%04lx size=%dx%d\n",
1054 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1055 lpitem->rect.bottom-lpitem->rect.top);
1056 return;
1059 if (lpitem->fType & MF_SEPARATOR)
1061 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1062 if( !menuBar)
1063 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1064 return;
1067 itemheight = 0;
1068 lpitem->xTab = 0;
1070 if (!menuBar) {
1071 if (lpitem->hbmpItem) {
1072 SIZE size;
1074 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1075 /* Keep the size of the bitmap in callback mode to be able
1076 * to draw it correctly */
1077 lpitem->bmpsize = size;
1078 lppop->textOffset = max( lppop->textOffset, size.cx);
1079 lpitem->rect.right += size.cx + 2;
1080 itemheight = size.cy + 2;
1082 if( !(lppop->dwStyle & MNS_NOCHECK))
1083 lpitem->rect.right += check_bitmap_width;
1084 lpitem->rect.right += 4 + menucharsize.cx;
1085 lpitem->xTab = lpitem->rect.right;
1086 lpitem->rect.right += arrow_bitmap_width;
1087 } else if (lpitem->hbmpItem) { /* menuBar */
1088 SIZE size;
1090 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1091 lpitem->bmpsize = size;
1092 lpitem->rect.right += size.cx;
1093 if( lpitem->text) lpitem->rect.right += 2;
1094 itemheight = size.cy;
1097 /* it must be a text item - unless it's the system menu */
1098 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1099 HFONT hfontOld = NULL;
1100 RECT rc = lpitem->rect;
1101 LONG txtheight, txtwidth;
1103 if ( lpitem->fState & MFS_DEFAULT ) {
1104 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1106 if (menuBar) {
1107 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1108 DT_SINGLELINE|DT_CALCRECT);
1109 lpitem->rect.right += rc.right - rc.left;
1110 itemheight = max( max( itemheight, txtheight),
1111 GetSystemMetrics( SM_CYMENU) - 1);
1112 lpitem->rect.right += 2 * menucharsize.cx;
1113 } else {
1114 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1115 RECT tmprc = rc;
1116 LONG tmpheight;
1117 int n = (int)( p - lpitem->text);
1118 /* Item contains a tab (only meaningful in popup menus) */
1119 /* get text size before the tab */
1120 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1121 DT_SINGLELINE|DT_CALCRECT);
1122 txtwidth = rc.right - rc.left;
1123 p += 1; /* advance past the Tab */
1124 /* get text size after the tab */
1125 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1126 DT_SINGLELINE|DT_CALCRECT);
1127 lpitem->xTab += txtwidth;
1128 txtheight = max( txtheight, tmpheight);
1129 txtwidth += menucharsize.cx + /* space for the tab */
1130 tmprc.right - tmprc.left; /* space for the short cut */
1131 } else {
1132 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1133 DT_SINGLELINE|DT_CALCRECT);
1134 txtwidth = rc.right - rc.left;
1135 lpitem->xTab += txtwidth;
1137 lpitem->rect.right += 2 + txtwidth;
1138 itemheight = max( itemheight,
1139 max( txtheight + 2, menucharsize.cy + 4));
1141 if (hfontOld) SelectObject (hdc, hfontOld);
1142 } else if( menuBar) {
1143 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1145 lpitem->rect.bottom += itemheight;
1146 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1150 /***********************************************************************
1151 * MENU_GetMaxPopupHeight
1153 static UINT
1154 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1156 if (lppop->cyMax)
1157 return lppop->cyMax;
1158 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1162 /***********************************************************************
1163 * MENU_PopupMenuCalcSize
1165 * Calculate the size of a popup menu.
1167 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1169 MENUITEM *lpitem;
1170 HDC hdc;
1171 UINT start, i;
1172 int textandbmp = FALSE;
1173 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1175 lppop->Width = lppop->Height = 0;
1176 if (lppop->nItems == 0) return;
1177 hdc = GetDC( 0 );
1179 SelectObject( hdc, get_menu_font(FALSE));
1181 start = 0;
1182 maxX = 2 + 1;
1184 lppop->textOffset = 0;
1186 while (start < lppop->nItems)
1188 lpitem = &lppop->items[start];
1189 orgX = maxX;
1190 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1191 orgX += MENU_COL_SPACE;
1192 orgY = MENU_TOP_MARGIN;
1194 maxTab = maxTabWidth = 0;
1195 /* Parse items until column break or end of menu */
1196 for (i = start; i < lppop->nItems; i++, lpitem++)
1198 if ((i != start) &&
1199 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1201 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1202 maxX = max( maxX, lpitem->rect.right );
1203 orgY = lpitem->rect.bottom;
1204 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1206 maxTab = max( maxTab, lpitem->xTab );
1207 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1209 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1212 /* Finish the column (set all items to the largest width found) */
1213 maxX = max( maxX, maxTab + maxTabWidth );
1214 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1216 lpitem->rect.right = maxX;
1217 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1218 lpitem->xTab = maxTab;
1221 lppop->Height = max( lppop->Height, orgY );
1224 lppop->Width = maxX;
1225 /* if none of the items have both text and bitmap then
1226 * the text and bitmaps are all aligned on the left. If there is at
1227 * least one item with both text and bitmap then bitmaps are
1228 * on the left and texts left aligned with the right hand side
1229 * of the bitmaps */
1230 if( !textandbmp) lppop->textOffset = 0;
1232 /* space for 3d border */
1233 lppop->Height += MENU_BOTTOM_MARGIN;
1234 lppop->Width += 2;
1236 /* Adjust popup height if it exceeds maximum */
1237 maxHeight = MENU_GetMaxPopupHeight(lppop);
1238 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1239 if (lppop->Height >= maxHeight)
1241 lppop->Height = maxHeight;
1242 lppop->bScrolling = TRUE;
1244 else
1246 lppop->bScrolling = FALSE;
1249 ReleaseDC( 0, hdc );
1253 /***********************************************************************
1254 * MENU_MenuBarCalcSize
1256 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1257 * height is off by 1 pixel which causes lengthy window relocations when
1258 * active document window is maximized/restored.
1260 * Calculate the size of the menu bar.
1262 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1263 LPPOPUPMENU lppop, HWND hwndOwner )
1265 MENUITEM *lpitem;
1266 UINT start, i, helpPos;
1267 int orgX, orgY, maxY;
1269 if ((lprect == NULL) || (lppop == NULL)) return;
1270 if (lppop->nItems == 0) return;
1271 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1272 lppop->Width = lprect->right - lprect->left;
1273 lppop->Height = 0;
1274 maxY = lprect->top+1;
1275 start = 0;
1276 helpPos = ~0U;
1277 lppop->textOffset = 0;
1278 while (start < lppop->nItems)
1280 lpitem = &lppop->items[start];
1281 orgX = lprect->left;
1282 orgY = maxY;
1284 /* Parse items until line break or end of menu */
1285 for (i = start; i < lppop->nItems; i++, lpitem++)
1287 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1288 if ((i != start) &&
1289 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1291 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1292 debug_print_menuitem (" item: ", lpitem, "");
1293 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1295 if (lpitem->rect.right > lprect->right)
1297 if (i != start) break;
1298 else lpitem->rect.right = lprect->right;
1300 maxY = max( maxY, lpitem->rect.bottom );
1301 orgX = lpitem->rect.right;
1304 /* Finish the line (set all items to the largest height found) */
1305 while (start < i) lppop->items[start++].rect.bottom = maxY;
1308 lprect->bottom = maxY;
1309 lppop->Height = lprect->bottom - lprect->top;
1311 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1312 /* the last item (if several lines, only move the last line) */
1313 if (helpPos == ~0U) return;
1314 lpitem = &lppop->items[lppop->nItems-1];
1315 orgY = lpitem->rect.top;
1316 orgX = lprect->right;
1317 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1318 if (lpitem->rect.top != orgY) break; /* Other line */
1319 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1320 lpitem->rect.left += orgX - lpitem->rect.right;
1321 lpitem->rect.right = orgX;
1322 orgX = lpitem->rect.left;
1327 /***********************************************************************
1328 * MENU_DrawScrollArrows
1330 * Draw scroll arrows.
1332 static void
1333 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1335 HDC hdcMem = CreateCompatibleDC(hdc);
1336 HBITMAP hOrigBitmap;
1337 UINT arrow_bitmap_width, arrow_bitmap_height;
1338 BITMAP bmp;
1339 RECT rect;
1341 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1342 arrow_bitmap_width = bmp.bmWidth;
1343 arrow_bitmap_height = bmp.bmHeight;
1346 if (lppop->nScrollPos)
1347 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1348 else
1349 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1350 rect.left = 0;
1351 rect.top = 0;
1352 rect.right = lppop->Width;
1353 rect.bottom = arrow_bitmap_height;
1354 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1355 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1356 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1357 rect.top = lppop->Height - arrow_bitmap_height;
1358 rect.bottom = lppop->Height;
1359 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1360 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1361 SelectObject(hdcMem, get_down_arrow_bitmap());
1362 else
1363 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1364 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1365 lppop->Height - arrow_bitmap_height,
1366 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1367 SelectObject(hdcMem, hOrigBitmap);
1368 DeleteDC(hdcMem);
1372 /***********************************************************************
1373 * draw_popup_arrow
1375 * Draws the popup-menu arrow.
1377 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1378 UINT arrow_bitmap_height)
1380 HDC hdcMem = CreateCompatibleDC( hdc );
1381 HBITMAP hOrigBitmap;
1383 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1384 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1385 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1386 arrow_bitmap_width, arrow_bitmap_height,
1387 hdcMem, 0, 0, SRCCOPY );
1388 SelectObject( hdcMem, hOrigBitmap );
1389 DeleteDC( hdcMem );
1391 /***********************************************************************
1392 * MENU_DrawMenuItem
1394 * Draw a single menu item.
1396 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1397 UINT height, BOOL menuBar, UINT odaction )
1399 RECT rect;
1400 BOOL flat_menu = FALSE;
1401 int bkgnd;
1402 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1403 POPUPMENU *menu = MENU_GetMenu(hmenu);
1404 RECT bmprc;
1406 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1408 if (!menuBar) {
1409 BITMAP bmp;
1410 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1411 arrow_bitmap_width = bmp.bmWidth;
1412 arrow_bitmap_height = bmp.bmHeight;
1415 if (lpitem->fType & MF_SYSMENU)
1417 if( !IsIconic(hwnd) )
1418 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1419 return;
1422 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1423 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1425 /* Setup colors */
1427 if (lpitem->fState & MF_HILITE)
1429 if(menuBar && !flat_menu) {
1430 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1431 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1432 } else {
1433 if(lpitem->fState & MF_GRAYED)
1434 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1435 else
1436 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1437 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1440 else
1442 if (lpitem->fState & MF_GRAYED)
1443 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1444 else
1445 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1446 SetBkColor( hdc, GetSysColor( bkgnd ) );
1449 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1450 rect = lpitem->rect;
1451 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1453 if (lpitem->fType & MF_OWNERDRAW)
1456 ** Experimentation under Windows reveals that an owner-drawn
1457 ** menu is given the rectangle which includes the space it requested
1458 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1459 ** and a popup-menu arrow. This is the value of lpitem->rect.
1460 ** Windows will leave all drawing to the application except for
1461 ** the popup-menu arrow. Windows always draws that itself, after
1462 ** the menu owner has finished drawing.
1464 DRAWITEMSTRUCT dis;
1466 dis.CtlType = ODT_MENU;
1467 dis.CtlID = 0;
1468 dis.itemID = lpitem->wID;
1469 dis.itemData = lpitem->dwItemData;
1470 dis.itemState = 0;
1471 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1472 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1473 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1474 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1475 dis.hwndItem = (HWND)hmenu;
1476 dis.hDC = hdc;
1477 dis.rcItem = rect;
1478 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1479 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1480 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1481 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1482 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1483 /* Draw the popup-menu arrow */
1484 if (lpitem->fType & MF_POPUP)
1485 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1486 arrow_bitmap_height);
1487 return;
1490 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1492 if (lpitem->fState & MF_HILITE)
1494 if (flat_menu)
1496 InflateRect (&rect, -1, -1);
1497 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1498 InflateRect (&rect, 1, 1);
1499 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1501 else
1503 if(menuBar)
1504 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1505 else
1506 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1509 else
1510 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1512 SetBkMode( hdc, TRANSPARENT );
1514 /* vertical separator */
1515 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1517 HPEN oldPen;
1518 RECT rc = rect;
1520 rc.left -= MENU_COL_SPACE / 2 + 1;
1521 rc.top = 3;
1522 rc.bottom = height - 3;
1523 if (flat_menu)
1525 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1526 MoveToEx( hdc, rc.left, rc.top, NULL );
1527 LineTo( hdc, rc.left, rc.bottom );
1528 SelectObject( hdc, oldPen );
1530 else
1531 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1534 /* horizontal separator */
1535 if (lpitem->fType & MF_SEPARATOR)
1537 HPEN oldPen;
1538 RECT rc = rect;
1540 rc.left++;
1541 rc.right--;
1542 rc.top = ( rc.top + rc.bottom) / 2;
1543 if (flat_menu)
1545 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1546 MoveToEx( hdc, rc.left, rc.top, NULL );
1547 LineTo( hdc, rc.right, rc.top );
1548 SelectObject( hdc, oldPen );
1550 else
1551 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1552 return;
1555 /* helper lines for debugging */
1556 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1557 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1558 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1559 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1562 if (lpitem->hbmpItem) {
1563 /* calculate the bitmap rectangle in coordinates relative
1564 * to the item rectangle */
1565 if( menuBar) {
1566 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1567 bmprc.left = 3;
1568 else
1569 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1571 else if (menu->dwStyle & MNS_NOCHECK)
1572 bmprc.left = 4;
1573 else if (menu->dwStyle & MNS_CHECKORBMP)
1574 bmprc.left = 2;
1575 else
1576 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1577 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1578 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1579 bmprc.top = 0;
1580 else
1581 bmprc.top = (rect.bottom - rect.top -
1582 lpitem->bmpsize.cy) / 2;
1583 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1586 if (!menuBar)
1588 HBITMAP bm;
1589 INT y = rect.top + rect.bottom;
1590 RECT rc = rect;
1591 int checked = FALSE;
1592 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1593 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1594 /* Draw the check mark
1596 * FIXME:
1597 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1599 if( !(menu->dwStyle & MNS_NOCHECK)) {
1600 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1601 lpitem->hUnCheckBit;
1602 if (bm) /* we have a custom bitmap */
1604 HDC hdcMem = CreateCompatibleDC( hdc );
1606 SelectObject( hdcMem, bm );
1607 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1608 check_bitmap_width, check_bitmap_height,
1609 hdcMem, 0, 0, SRCCOPY );
1610 DeleteDC( hdcMem );
1611 checked = TRUE;
1613 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1615 RECT r;
1616 HBITMAP bm = CreateBitmap( check_bitmap_width,
1617 check_bitmap_height, 1, 1, NULL );
1618 HDC hdcMem = CreateCompatibleDC( hdc );
1620 SelectObject( hdcMem, bm );
1621 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1622 DrawFrameControl( hdcMem, &r, DFC_MENU,
1623 (lpitem->fType & MFT_RADIOCHECK) ?
1624 DFCS_MENUBULLET : DFCS_MENUCHECK );
1625 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1626 hdcMem, 0, 0, SRCCOPY );
1627 DeleteDC( hdcMem );
1628 DeleteObject( bm );
1629 checked = TRUE;
1632 if( lpitem->hbmpItem &&
1633 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1634 POINT origorg;
1635 /* some applications make this assumption on the DC's origin */
1636 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1637 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1638 odaction, FALSE);
1639 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1641 /* Draw the popup-menu arrow */
1642 if (lpitem->fType & MF_POPUP)
1643 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1644 arrow_bitmap_height);
1645 rect.left += 4;
1646 if( !(menu->dwStyle & MNS_NOCHECK))
1647 rect.left += check_bitmap_width;
1648 rect.right -= arrow_bitmap_width;
1650 else if( lpitem->hbmpItem)
1651 { /* Draw the bitmap */
1652 POINT origorg;
1654 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1655 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1656 odaction, menuBar);
1657 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1659 /* process text if present */
1660 if (lpitem->text)
1662 register int i;
1663 HFONT hfontOld = 0;
1665 UINT uFormat = (menuBar) ?
1666 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1667 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1669 if( !(menu->dwStyle & MNS_CHECKORBMP))
1670 rect.left += menu->textOffset;
1672 if ( lpitem->fState & MFS_DEFAULT )
1674 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1677 if (menuBar) {
1678 if( lpitem->hbmpItem)
1679 rect.left += lpitem->bmpsize.cx;
1680 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1681 rect.left += menucharsize.cx;
1682 rect.right -= menucharsize.cx;
1685 for (i = 0; lpitem->text[i]; i++)
1686 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1687 break;
1689 if(lpitem->fState & MF_GRAYED)
1691 if (!(lpitem->fState & MF_HILITE) )
1693 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1694 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1695 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1696 --rect.left; --rect.top; --rect.right; --rect.bottom;
1698 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1701 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1703 /* paint the shortcut text */
1704 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1706 if (lpitem->text[i] == '\t')
1708 rect.left = lpitem->xTab;
1709 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1711 else
1713 rect.right = lpitem->xTab;
1714 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1717 if(lpitem->fState & MF_GRAYED)
1719 if (!(lpitem->fState & MF_HILITE) )
1721 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1722 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1723 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1724 --rect.left; --rect.top; --rect.right; --rect.bottom;
1726 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1728 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1731 if (hfontOld)
1732 SelectObject (hdc, hfontOld);
1737 /***********************************************************************
1738 * MENU_DrawPopupMenu
1740 * Paint a popup menu.
1742 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1744 HBRUSH hPrevBrush = 0;
1745 RECT rect;
1747 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1749 GetClientRect( hwnd, &rect );
1751 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1752 && (SelectObject( hdc, get_menu_font(FALSE))))
1754 HPEN hPrevPen;
1756 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1758 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1759 if( hPrevPen )
1761 POPUPMENU *menu;
1762 BOOL flat_menu = FALSE;
1764 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1765 if (flat_menu)
1766 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1767 else
1768 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1770 if( (menu = MENU_GetMenu( hmenu )))
1772 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1773 /* draw menu items */
1774 if( menu->nItems)
1776 MENUITEM *item;
1777 UINT u;
1779 item = menu->items;
1780 for( u = menu->nItems; u > 0; u--, item++)
1781 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1782 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1784 /* draw scroll arrows */
1785 if (menu->bScrolling)
1786 MENU_DrawScrollArrows(menu, hdc);
1788 } else
1790 SelectObject( hdc, hPrevBrush );
1795 /***********************************************************************
1796 * MENU_DrawMenuBar
1798 * Paint a menu bar. Returns the height of the menu bar.
1799 * called from [windows/nonclient.c]
1801 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1802 BOOL suppress_draw)
1804 LPPOPUPMENU lppop;
1805 HFONT hfontOld = 0;
1806 HMENU hMenu = GetMenu(hwnd);
1808 lppop = MENU_GetMenu( hMenu );
1809 if (lppop == NULL || lprect == NULL)
1811 return GetSystemMetrics(SM_CYMENU);
1814 if (suppress_draw)
1816 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1818 if (lppop->Height == 0)
1819 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1821 lprect->bottom = lprect->top + lppop->Height;
1823 if (hfontOld) SelectObject( hDC, hfontOld);
1824 return lppop->Height;
1826 else
1827 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1831 /***********************************************************************
1832 * MENU_ShowPopup
1834 * Display a popup menu.
1836 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1837 INT x, INT y, INT xanchor, INT yanchor )
1839 POPUPMENU *menu;
1840 INT width, height;
1841 POINT pt;
1842 HMONITOR monitor;
1843 MONITORINFO info;
1844 DWORD ex_style = 0;
1846 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1847 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1849 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1850 if (menu->FocusedItem != NO_SELECTED_ITEM)
1852 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1853 menu->FocusedItem = NO_SELECTED_ITEM;
1856 /* store the owner for DrawItem */
1857 if (!IsWindow( hwndOwner ))
1859 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1860 return FALSE;
1862 menu->hwndOwner = hwndOwner;
1864 menu->nScrollPos = 0;
1865 MENU_PopupMenuCalcSize( menu );
1867 /* adjust popup menu pos so that it fits within the desktop */
1869 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1870 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1872 /* FIXME: should use item rect */
1873 pt.x = x;
1874 pt.y = y;
1875 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1876 info.cbSize = sizeof(info);
1877 GetMonitorInfoW( monitor, &info );
1879 if (flags & TPM_LAYOUTRTL)
1881 ex_style = WS_EX_LAYOUTRTL;
1882 flags ^= TPM_RIGHTALIGN;
1885 if( flags & TPM_RIGHTALIGN ) x -= width;
1886 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1888 if( flags & TPM_BOTTOMALIGN ) y -= height;
1889 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1891 if( x + width > info.rcWork.right)
1893 if( xanchor && x >= width - xanchor )
1894 x -= width - xanchor;
1896 if( x + width > info.rcWork.right)
1897 x = info.rcWork.right - width;
1899 if( x < info.rcWork.left ) x = info.rcWork.left;
1901 if( y + height > info.rcWork.bottom)
1903 if( yanchor && y >= height + yanchor )
1904 y -= height + yanchor;
1906 if( y + height > info.rcWork.bottom)
1907 y = info.rcWork.bottom - height;
1909 if( y < info.rcWork.top ) y = info.rcWork.top;
1911 /* NOTE: In Windows, top menu popup is not owned. */
1912 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1913 WS_POPUP, x, y, width, height,
1914 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1915 (LPVOID)hmenu );
1916 if( !menu->hWnd ) return FALSE;
1917 if (!top_popup) {
1918 top_popup = menu->hWnd;
1919 top_popup_hmenu = hmenu;
1921 /* Display the window */
1923 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1924 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1925 UpdateWindow( menu->hWnd );
1926 return TRUE;
1930 /***********************************************************************
1931 * MENU_EnsureMenuItemVisible
1933 static void
1934 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1936 if (lppop->bScrolling)
1938 MENUITEM *item = &lppop->items[wIndex];
1939 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1940 UINT nOldPos = lppop->nScrollPos;
1941 RECT rc;
1942 UINT arrow_bitmap_height;
1943 BITMAP bmp;
1945 GetClientRect(lppop->hWnd, &rc);
1947 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1948 arrow_bitmap_height = bmp.bmHeight;
1950 rc.top += arrow_bitmap_height;
1951 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1953 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1954 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1957 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1958 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1959 MENU_DrawScrollArrows(lppop, hdc);
1961 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1963 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1964 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1965 MENU_DrawScrollArrows(lppop, hdc);
1971 /***********************************************************************
1972 * MENU_SelectItem
1974 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1975 BOOL sendMenuSelect, HMENU topmenu )
1977 LPPOPUPMENU lppop;
1978 HDC hdc;
1980 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1982 lppop = MENU_GetMenu( hmenu );
1983 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1985 if (lppop->FocusedItem == wIndex) return;
1986 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1987 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1988 if (!top_popup) {
1989 top_popup = lppop->hWnd;
1990 top_popup_hmenu = hmenu;
1993 SelectObject( hdc, get_menu_font(FALSE));
1995 /* Clear previous highlighted item */
1996 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1998 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1999 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2000 lppop->Height, !(lppop->wFlags & MF_POPUP),
2001 ODA_SELECT );
2004 /* Highlight new item (if any) */
2005 lppop->FocusedItem = wIndex;
2006 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2008 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2009 lppop->items[wIndex].fState |= MF_HILITE;
2010 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2011 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2012 &lppop->items[wIndex], lppop->Height,
2013 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2015 if (sendMenuSelect)
2017 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2018 SendMessageW( hwndOwner, WM_MENUSELECT,
2019 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2020 ip->fType | ip->fState |
2021 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2024 else if (sendMenuSelect) {
2025 if(topmenu){
2026 int pos;
2027 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2028 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2029 MENUITEM *ip = &ptm->items[pos];
2030 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2031 ip->fType | ip->fState |
2032 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2036 ReleaseDC( lppop->hWnd, hdc );
2040 /***********************************************************************
2041 * MENU_MoveSelection
2043 * Moves currently selected item according to the offset parameter.
2044 * If there is no selection then it should select the last item if
2045 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2047 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2049 INT i;
2050 POPUPMENU *menu;
2052 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2054 menu = MENU_GetMenu( hmenu );
2055 if ((!menu) || (!menu->items)) return;
2057 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2059 if( menu->nItems == 1 ) return; else
2060 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2061 ; i += offset)
2062 if (!(menu->items[i].fType & MF_SEPARATOR))
2064 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2065 return;
2069 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2070 i >= 0 && i < menu->nItems ; i += offset)
2071 if (!(menu->items[i].fType & MF_SEPARATOR))
2073 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2074 return;
2079 /**********************************************************************
2080 * MENU_InsertItem
2082 * Insert (allocate) a new item into a menu.
2084 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2086 MENUITEM *newItems;
2087 POPUPMENU *menu;
2089 if (!(menu = MENU_GetMenu(hMenu)))
2090 return NULL;
2092 /* Find where to insert new item */
2094 if (flags & MF_BYPOSITION) {
2095 if (pos > menu->nItems)
2096 pos = menu->nItems;
2097 } else {
2098 if (!MENU_FindItem( &hMenu, &pos, flags ))
2099 pos = menu->nItems;
2100 else {
2101 if (!(menu = MENU_GetMenu( hMenu )))
2102 return NULL;
2106 /* Make sure that MDI system buttons stay on the right side.
2107 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2108 * regardless of their id.
2110 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2111 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2112 pos--;
2114 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2116 /* Create new items array */
2118 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2119 if (!newItems)
2121 WARN("allocation failed\n" );
2122 return NULL;
2124 if (menu->nItems > 0)
2126 /* Copy the old array into the new one */
2127 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2128 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2129 (menu->nItems-pos)*sizeof(MENUITEM) );
2130 HeapFree( GetProcessHeap(), 0, menu->items );
2132 menu->items = newItems;
2133 menu->nItems++;
2134 memset( &newItems[pos], 0, sizeof(*newItems) );
2135 menu->Height = 0; /* force size recalculate */
2136 return &newItems[pos];
2140 /**********************************************************************
2141 * MENU_ParseResource
2143 * Parse a standard menu resource and add items to the menu.
2144 * Return a pointer to the end of the resource.
2146 * NOTE: flags is equivalent to the mtOption field
2148 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2150 WORD flags, id = 0;
2151 LPCWSTR str;
2152 BOOL end_flag;
2156 flags = GET_WORD(res);
2157 end_flag = flags & MF_END;
2158 /* Remove MF_END because it has the same value as MF_HILITE */
2159 flags &= ~MF_END;
2160 res += sizeof(WORD);
2161 if (!(flags & MF_POPUP))
2163 id = GET_WORD(res);
2164 res += sizeof(WORD);
2166 str = (LPCWSTR)res;
2167 res += (strlenW(str) + 1) * sizeof(WCHAR);
2168 if (flags & MF_POPUP)
2170 HMENU hSubMenu = CreatePopupMenu();
2171 if (!hSubMenu) return NULL;
2172 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2173 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2175 else /* Not a popup */
2177 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2179 } while (!end_flag);
2180 return res;
2184 /**********************************************************************
2185 * MENUEX_ParseResource
2187 * Parse an extended menu resource and add items to the menu.
2188 * Return a pointer to the end of the resource.
2190 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2192 WORD resinfo;
2193 do {
2194 MENUITEMINFOW mii;
2196 mii.cbSize = sizeof(mii);
2197 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2198 mii.fType = GET_DWORD(res);
2199 res += sizeof(DWORD);
2200 mii.fState = GET_DWORD(res);
2201 res += sizeof(DWORD);
2202 mii.wID = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2205 res += sizeof(WORD);
2206 /* Align the text on a word boundary. */
2207 res += (~((UINT_PTR)res - 1)) & 1;
2208 mii.dwTypeData = (LPWSTR) res;
2209 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2210 /* Align the following fields on a dword boundary. */
2211 res += (~((UINT_PTR)res - 1)) & 3;
2213 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2214 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2216 if (resinfo & 1) { /* Pop-up? */
2217 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2218 res += sizeof(DWORD);
2219 mii.hSubMenu = CreatePopupMenu();
2220 if (!mii.hSubMenu)
2221 return NULL;
2222 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2223 DestroyMenu(mii.hSubMenu);
2224 return NULL;
2226 mii.fMask |= MIIM_SUBMENU;
2227 mii.fType |= MF_POPUP;
2229 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2231 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2232 mii.wID, mii.fType);
2233 mii.fType |= MF_SEPARATOR;
2235 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2236 } while (!(resinfo & MF_END));
2237 return res;
2241 /***********************************************************************
2242 * MENU_GetSubPopup
2244 * Return the handle of the selected sub-popup menu (if any).
2246 static HMENU MENU_GetSubPopup( HMENU hmenu )
2248 POPUPMENU *menu;
2249 MENUITEM *item;
2251 menu = MENU_GetMenu( hmenu );
2253 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2255 item = &menu->items[menu->FocusedItem];
2256 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2257 return item->hSubMenu;
2258 return 0;
2262 /***********************************************************************
2263 * MENU_HideSubPopups
2265 * Hide the sub-popup menus of this menu.
2267 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2268 BOOL sendMenuSelect, UINT wFlags )
2270 POPUPMENU *menu = MENU_GetMenu( hmenu );
2272 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2274 if (menu && top_popup)
2276 HMENU hsubmenu;
2277 POPUPMENU *submenu;
2278 MENUITEM *item;
2280 if (menu->FocusedItem != NO_SELECTED_ITEM)
2282 item = &menu->items[menu->FocusedItem];
2283 if (!(item->fType & MF_POPUP) ||
2284 !(item->fState & MF_MOUSESELECT)) return;
2285 item->fState &= ~MF_MOUSESELECT;
2286 hsubmenu = item->hSubMenu;
2287 } else return;
2289 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2290 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2291 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2292 DestroyWindow( submenu->hWnd );
2293 submenu->hWnd = 0;
2295 if (!(wFlags & TPM_NONOTIFY))
2296 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2297 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2302 /***********************************************************************
2303 * MENU_ShowSubPopup
2305 * Display the sub-menu of the selected item of this menu.
2306 * Return the handle of the submenu, or hmenu if no submenu to display.
2308 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2309 BOOL selectFirst, UINT wFlags )
2311 RECT rect;
2312 POPUPMENU *menu;
2313 MENUITEM *item;
2314 HDC hdc;
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2318 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2320 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2322 item = &menu->items[menu->FocusedItem];
2323 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2324 return hmenu;
2326 /* message must be sent before using item,
2327 because nearly everything may be changed by the application ! */
2329 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2330 if (!(wFlags & TPM_NONOTIFY))
2331 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2332 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2334 item = &menu->items[menu->FocusedItem];
2335 rect = item->rect;
2337 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2338 if (!(item->fState & MF_HILITE))
2340 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2341 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2343 SelectObject( hdc, get_menu_font(FALSE));
2345 item->fState |= MF_HILITE;
2346 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2347 ReleaseDC( menu->hWnd, hdc );
2349 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2350 item->rect = rect;
2352 item->fState |= MF_MOUSESELECT;
2354 if (IS_SYSTEM_MENU(menu))
2356 MENU_InitSysMenuPopup(item->hSubMenu,
2357 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2358 GetClassLongW( menu->hWnd, GCL_STYLE));
2360 NC_GetSysPopupPos( menu->hWnd, &rect );
2361 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2362 rect.top = rect.bottom;
2363 rect.right = GetSystemMetrics(SM_CXSIZE);
2364 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2366 else
2368 GetWindowRect( menu->hWnd, &rect );
2369 if (menu->wFlags & MF_POPUP)
2371 RECT rc = item->rect;
2373 MENU_AdjustMenuItemRect(menu, &rc);
2375 /* The first item in the popup menu has to be at the
2376 same y position as the focused menu item */
2377 if (wFlags & TPM_LAYOUTRTL)
2378 rect.left += GetSystemMetrics(SM_CXBORDER);
2379 else
2380 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2381 rect.top += rc.top - MENU_TOP_MARGIN;
2382 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2383 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2384 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2386 else
2388 if (wFlags & TPM_LAYOUTRTL)
2389 rect.left = rect.right - item->rect.left;
2390 else
2391 rect.left += item->rect.left;
2392 rect.top += item->rect.bottom;
2393 rect.right = item->rect.right - item->rect.left;
2394 rect.bottom = item->rect.bottom - item->rect.top;
2398 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2399 rect.left, rect.top, rect.right, rect.bottom );
2400 if (selectFirst)
2401 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2402 return item->hSubMenu;
2407 /**********************************************************************
2408 * MENU_IsMenuActive
2410 HWND MENU_IsMenuActive(void)
2412 return top_popup;
2415 /**********************************************************************
2416 * MENU_EndMenu
2418 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2420 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2422 void MENU_EndMenu( HWND hwnd )
2424 POPUPMENU *menu;
2425 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2426 if (menu && hwnd == menu->hwndOwner) EndMenu();
2429 /***********************************************************************
2430 * MENU_PtMenu
2432 * Walks menu chain trying to find a menu pt maps to.
2434 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2436 POPUPMENU *menu = MENU_GetMenu( hMenu );
2437 UINT item = menu->FocusedItem;
2438 HMENU ret;
2440 /* try subpopup first (if any) */
2441 ret = (item != NO_SELECTED_ITEM &&
2442 (menu->items[item].fType & MF_POPUP) &&
2443 (menu->items[item].fState & MF_MOUSESELECT))
2444 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2446 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2448 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2449 if( menu->wFlags & MF_POPUP )
2451 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2453 else if (ht == HTSYSMENU)
2454 ret = get_win_sys_menu( menu->hWnd );
2455 else if (ht == HTMENU)
2456 ret = GetMenu( menu->hWnd );
2458 return ret;
2461 /***********************************************************************
2462 * MENU_ExecFocusedItem
2464 * Execute a menu item (for instance when user pressed Enter).
2465 * Return the wID of the executed item. Otherwise, -1 indicating
2466 * that no menu item was executed, -2 if a popup is shown;
2467 * Have to receive the flags for the TrackPopupMenu options to avoid
2468 * sending unwanted message.
2471 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2473 MENUITEM *item;
2474 POPUPMENU *menu = MENU_GetMenu( hMenu );
2476 TRACE("%p hmenu=%p\n", pmt, hMenu);
2478 if (!menu || !menu->nItems ||
2479 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2481 item = &menu->items[menu->FocusedItem];
2483 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2485 if (!(item->fType & MF_POPUP))
2487 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2489 /* If TPM_RETURNCMD is set you return the id, but
2490 do not send a message to the owner */
2491 if(!(wFlags & TPM_RETURNCMD))
2493 if( menu->wFlags & MF_SYSMENU )
2494 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2495 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2496 else
2498 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2499 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2501 if (dwStyle & MNS_NOTIFYBYPOS)
2502 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2503 (LPARAM)hMenu);
2504 else
2505 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2508 return item->wID;
2511 else
2513 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2514 return -2;
2517 return -1;
2520 /***********************************************************************
2521 * MENU_SwitchTracking
2523 * Helper function for menu navigation routines.
2525 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2527 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2528 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2530 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2532 if( pmt->hTopMenu != hPtMenu &&
2533 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2535 /* both are top level menus (system and menu-bar) */
2536 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2537 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2538 pmt->hTopMenu = hPtMenu;
2540 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2541 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2545 /***********************************************************************
2546 * MENU_ButtonDown
2548 * Return TRUE if we can go on with menu tracking.
2550 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2552 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2554 if (hPtMenu)
2556 UINT id = 0;
2557 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2558 MENUITEM *item;
2560 if( IS_SYSTEM_MENU(ptmenu) )
2561 item = ptmenu->items;
2562 else
2563 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2565 if( item )
2567 if( ptmenu->FocusedItem != id )
2568 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2570 /* If the popup menu is not already "popped" */
2571 if(!(item->fState & MF_MOUSESELECT ))
2573 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2576 return TRUE;
2578 /* Else the click was on the menu bar, finish the tracking */
2580 return FALSE;
2583 /***********************************************************************
2584 * MENU_ButtonUp
2586 * Return the value of MENU_ExecFocusedItem if
2587 * the selected item was not a popup. Else open the popup.
2588 * A -1 return value indicates that we go on with menu tracking.
2591 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2593 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2595 if (hPtMenu)
2597 UINT id = 0;
2598 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2599 MENUITEM *item;
2601 if( IS_SYSTEM_MENU(ptmenu) )
2602 item = ptmenu->items;
2603 else
2604 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2606 if( item && (ptmenu->FocusedItem == id ))
2608 debug_print_menuitem ("FocusedItem: ", item, "");
2610 if( !(item->fType & MF_POPUP) )
2612 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2613 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2614 return executedMenuId;
2617 /* If we are dealing with the top-level menu */
2618 /* and this is a click on an already "popped" item: */
2619 /* Stop the menu tracking and close the opened submenus */
2620 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2621 return 0;
2623 ptmenu->bTimeToHide = TRUE;
2625 return -1;
2629 /***********************************************************************
2630 * MENU_MouseMove
2632 * Return TRUE if we can go on with menu tracking.
2634 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2636 UINT id = NO_SELECTED_ITEM;
2637 POPUPMENU *ptmenu = NULL;
2639 if( hPtMenu )
2641 ptmenu = MENU_GetMenu( hPtMenu );
2642 if( IS_SYSTEM_MENU(ptmenu) )
2643 id = 0;
2644 else
2645 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2648 if( id == NO_SELECTED_ITEM )
2650 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2651 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2654 else if( ptmenu->FocusedItem != id )
2656 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2657 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2659 return TRUE;
2663 /***********************************************************************
2664 * MENU_DoNextMenu
2666 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2668 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2670 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2671 BOOL atEnd = FALSE;
2673 /* When skipping left, we need to do something special after the
2674 first menu. */
2675 if (vk == VK_LEFT && menu->FocusedItem == 0)
2677 atEnd = TRUE;
2679 /* When skipping right, for the non-system menu, we need to
2680 handle the last non-special menu item (ie skip any window
2681 icons such as MDI maximize, restore or close) */
2682 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2684 UINT i = menu->FocusedItem + 1;
2685 while (i < menu->nItems) {
2686 if ((menu->items[i].wID >= SC_SIZE &&
2687 menu->items[i].wID <= SC_RESTORE)) {
2688 i++;
2689 } else break;
2691 if (i == menu->nItems) {
2692 atEnd = TRUE;
2695 /* When skipping right, we need to cater for the system menu */
2696 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2698 if (menu->FocusedItem == (menu->nItems - 1)) {
2699 atEnd = TRUE;
2703 if( atEnd )
2705 MDINEXTMENU next_menu;
2706 HMENU hNewMenu;
2707 HWND hNewWnd;
2708 UINT id = 0;
2710 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2711 next_menu.hmenuNext = 0;
2712 next_menu.hwndNext = 0;
2713 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2715 TRACE("%p [%p] -> %p [%p]\n",
2716 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2718 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2720 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2721 hNewWnd = pmt->hOwnerWnd;
2722 if( IS_SYSTEM_MENU(menu) )
2724 /* switch to the menu bar */
2726 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2728 if( vk == VK_LEFT )
2730 menu = MENU_GetMenu( hNewMenu );
2731 id = menu->nItems - 1;
2733 /* Skip backwards over any system predefined icons,
2734 eg. MDI close, restore etc icons */
2735 while ((id > 0) &&
2736 (menu->items[id].wID >= SC_SIZE &&
2737 menu->items[id].wID <= SC_RESTORE)) id--;
2740 else if (style & WS_SYSMENU )
2742 /* switch to the system menu */
2743 hNewMenu = get_win_sys_menu( hNewWnd );
2745 else return FALSE;
2747 else /* application returned a new menu to switch to */
2749 hNewMenu = next_menu.hmenuNext;
2750 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2752 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2754 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2756 if (style & WS_SYSMENU &&
2757 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2759 /* get the real system menu */
2760 hNewMenu = get_win_sys_menu(hNewWnd);
2762 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2764 /* FIXME: Not sure what to do here;
2765 * perhaps try to track hNewMenu as a popup? */
2767 TRACE(" -- got confused.\n");
2768 return FALSE;
2771 else return FALSE;
2774 if( hNewMenu != pmt->hTopMenu )
2776 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2777 FALSE, 0 );
2778 if( pmt->hCurrentMenu != pmt->hTopMenu )
2779 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2782 if( hNewWnd != pmt->hOwnerWnd )
2784 pmt->hOwnerWnd = hNewWnd;
2785 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2788 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2789 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2791 return TRUE;
2793 return FALSE;
2796 /***********************************************************************
2797 * MENU_SuspendPopup
2799 * The idea is not to show the popup if the next input message is
2800 * going to hide it anyway.
2802 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2804 MSG msg;
2806 msg.hwnd = pmt->hOwnerWnd;
2808 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2809 pmt->trackFlags |= TF_SKIPREMOVE;
2811 switch( uMsg )
2813 case WM_KEYDOWN:
2814 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2815 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2817 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2818 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2819 if( msg.message == WM_KEYDOWN &&
2820 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2822 pmt->trackFlags |= TF_SUSPENDPOPUP;
2823 return TRUE;
2826 break;
2829 /* failures go through this */
2830 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2831 return FALSE;
2834 /***********************************************************************
2835 * MENU_KeyEscape
2837 * Handle a VK_ESCAPE key event in a menu.
2839 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2841 BOOL bEndMenu = TRUE;
2843 if (pmt->hCurrentMenu != pmt->hTopMenu)
2845 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2847 if (menu->wFlags & MF_POPUP)
2849 HMENU hmenutmp, hmenuprev;
2851 hmenuprev = hmenutmp = pmt->hTopMenu;
2853 /* close topmost popup */
2854 while (hmenutmp != pmt->hCurrentMenu)
2856 hmenuprev = hmenutmp;
2857 hmenutmp = MENU_GetSubPopup( hmenuprev );
2860 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2861 pmt->hCurrentMenu = hmenuprev;
2862 bEndMenu = FALSE;
2866 return bEndMenu;
2869 /***********************************************************************
2870 * MENU_KeyLeft
2872 * Handle a VK_LEFT key event in a menu.
2874 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2876 POPUPMENU *menu;
2877 HMENU hmenutmp, hmenuprev;
2878 UINT prevcol;
2880 hmenuprev = hmenutmp = pmt->hTopMenu;
2881 menu = MENU_GetMenu( hmenutmp );
2883 /* Try to move 1 column left (if possible) */
2884 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2885 NO_SELECTED_ITEM ) {
2887 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2888 prevcol, TRUE, 0 );
2889 return;
2892 /* close topmost popup */
2893 while (hmenutmp != pmt->hCurrentMenu)
2895 hmenuprev = hmenutmp;
2896 hmenutmp = MENU_GetSubPopup( hmenuprev );
2899 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2900 pmt->hCurrentMenu = hmenuprev;
2902 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2904 /* move menu bar selection if no more popups are left */
2906 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2907 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2909 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2911 /* A sublevel menu was displayed - display the next one
2912 * unless there is another displacement coming up */
2914 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2915 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2916 pmt->hTopMenu, TRUE, wFlags);
2922 /***********************************************************************
2923 * MENU_KeyRight
2925 * Handle a VK_RIGHT key event in a menu.
2927 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2929 HMENU hmenutmp;
2930 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2931 UINT nextcol;
2933 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2934 pmt->hCurrentMenu,
2935 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2936 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2938 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2940 /* If already displaying a popup, try to display sub-popup */
2942 hmenutmp = pmt->hCurrentMenu;
2943 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2945 /* if subpopup was displayed then we are done */
2946 if (hmenutmp != pmt->hCurrentMenu) return;
2949 /* Check to see if there's another column */
2950 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2951 NO_SELECTED_ITEM ) {
2952 TRACE("Going to %d.\n", nextcol );
2953 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2954 nextcol, TRUE, 0 );
2955 return;
2958 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2960 if( pmt->hCurrentMenu != pmt->hTopMenu )
2962 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2963 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2964 } else hmenutmp = 0;
2966 /* try to move to the next item */
2967 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2968 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2970 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2971 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2972 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2973 pmt->hTopMenu, TRUE, wFlags);
2977 static void CALLBACK release_capture( BOOL __normal )
2979 set_capture_window( 0, GUI_INMENUMODE, NULL );
2982 /***********************************************************************
2983 * MENU_TrackMenu
2985 * Menu tracking code.
2987 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2988 HWND hwnd, const RECT *lprect )
2990 MSG msg;
2991 POPUPMENU *menu;
2992 BOOL fRemove;
2993 INT executedMenuId = -1;
2994 MTRACKER mt;
2995 BOOL enterIdleSent = FALSE;
2996 HWND capture_win;
2998 mt.trackFlags = 0;
2999 mt.hCurrentMenu = hmenu;
3000 mt.hTopMenu = hmenu;
3001 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3002 mt.pt.x = x;
3003 mt.pt.y = y;
3005 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3006 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3008 fEndMenu = FALSE;
3009 if (!(menu = MENU_GetMenu( hmenu )))
3011 WARN("Invalid menu handle %p\n", hmenu);
3012 SetLastError(ERROR_INVALID_MENU_HANDLE);
3013 return FALSE;
3016 if (wFlags & TPM_BUTTONDOWN)
3018 /* Get the result in order to start the tracking or not */
3019 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3020 fEndMenu = !fRemove;
3023 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3025 /* owner may not be visible when tracking a popup, so use the menu itself */
3026 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3027 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3029 __TRY while (!fEndMenu)
3031 menu = MENU_GetMenu( mt.hCurrentMenu );
3032 if (!menu) /* sometimes happens if I do a window manager close */
3033 break;
3035 /* we have to keep the message in the queue until it's
3036 * clear that menu loop is not over yet. */
3038 for (;;)
3040 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3042 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3043 /* remove the message from the queue */
3044 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3046 else
3048 if (!enterIdleSent)
3050 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3051 enterIdleSent = TRUE;
3052 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3054 WaitMessage();
3058 /* check if EndMenu() tried to cancel us, by posting this message */
3059 if(msg.message == WM_CANCELMODE)
3061 /* we are now out of the loop */
3062 fEndMenu = TRUE;
3064 /* remove the message from the queue */
3065 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3067 /* break out of internal loop, ala ESCAPE */
3068 break;
3071 TranslateMessage( &msg );
3072 mt.pt = msg.pt;
3074 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3075 enterIdleSent=FALSE;
3077 fRemove = FALSE;
3078 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3081 * Use the mouse coordinates in lParam instead of those in the MSG
3082 * struct to properly handle synthetic messages. They are already
3083 * in screen coordinates.
3085 mt.pt.x = (short)LOWORD(msg.lParam);
3086 mt.pt.y = (short)HIWORD(msg.lParam);
3088 /* Find a menu for this mouse event */
3089 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3091 switch(msg.message)
3093 /* no WM_NC... messages in captured state */
3095 case WM_RBUTTONDBLCLK:
3096 case WM_RBUTTONDOWN:
3097 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3098 /* fall through */
3099 case WM_LBUTTONDBLCLK:
3100 case WM_LBUTTONDOWN:
3101 /* If the message belongs to the menu, removes it from the queue */
3102 /* Else, end menu tracking */
3103 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3104 fEndMenu = !fRemove;
3105 break;
3107 case WM_RBUTTONUP:
3108 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3109 /* fall through */
3110 case WM_LBUTTONUP:
3111 /* Check if a menu was selected by the mouse */
3112 if (hmenu)
3114 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3115 TRACE("executedMenuId %d\n", executedMenuId);
3117 /* End the loop if executedMenuId is an item ID */
3118 /* or if the job was done (executedMenuId = 0). */
3119 fEndMenu = fRemove = (executedMenuId != -1);
3121 /* No menu was selected by the mouse */
3122 /* if the function was called by TrackPopupMenu, continue
3123 with the menu tracking. If not, stop it */
3124 else
3125 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3127 break;
3129 case WM_MOUSEMOVE:
3130 /* the selected menu item must be changed every time */
3131 /* the mouse moves. */
3133 if (hmenu)
3134 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3136 } /* switch(msg.message) - mouse */
3138 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3140 fRemove = TRUE; /* Keyboard messages are always removed */
3141 switch(msg.message)
3143 case WM_KEYDOWN:
3144 case WM_SYSKEYDOWN:
3145 switch(msg.wParam)
3147 case VK_MENU:
3148 case VK_F10:
3149 fEndMenu = TRUE;
3150 break;
3152 case VK_HOME:
3153 case VK_END:
3154 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3155 NO_SELECTED_ITEM, FALSE, 0 );
3156 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3157 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3158 break;
3160 case VK_UP:
3161 case VK_DOWN: /* If on menu bar, pull-down the menu */
3163 menu = MENU_GetMenu( mt.hCurrentMenu );
3164 if (!(menu->wFlags & MF_POPUP))
3165 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3166 else /* otherwise try to move selection */
3167 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3168 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3169 break;
3171 case VK_LEFT:
3172 MENU_KeyLeft( &mt, wFlags );
3173 break;
3175 case VK_RIGHT:
3176 MENU_KeyRight( &mt, wFlags );
3177 break;
3179 case VK_ESCAPE:
3180 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3181 break;
3183 case VK_F1:
3185 HELPINFO hi;
3186 hi.cbSize = sizeof(HELPINFO);
3187 hi.iContextType = HELPINFO_MENUITEM;
3188 if (menu->FocusedItem == NO_SELECTED_ITEM)
3189 hi.iCtrlId = 0;
3190 else
3191 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3192 hi.hItemHandle = hmenu;
3193 hi.dwContextId = menu->dwContextHelpID;
3194 hi.MousePos = msg.pt;
3195 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3196 break;
3199 default:
3200 break;
3202 break; /* WM_KEYDOWN */
3204 case WM_CHAR:
3205 case WM_SYSCHAR:
3207 UINT pos;
3209 if (msg.wParam == '\r' || msg.wParam == ' ')
3211 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3212 fEndMenu = (executedMenuId != -2);
3214 break;
3217 /* Hack to avoid control chars. */
3218 /* We will find a better way real soon... */
3219 if (msg.wParam < 32) break;
3221 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3222 LOWORD(msg.wParam), FALSE );
3223 if (pos == (UINT)-2) fEndMenu = TRUE;
3224 else if (pos == (UINT)-1) MessageBeep(0);
3225 else
3227 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3228 TRUE, 0 );
3229 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3230 fEndMenu = (executedMenuId != -2);
3233 break;
3234 } /* switch(msg.message) - kbd */
3236 else
3238 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3239 DispatchMessageW( &msg );
3240 continue;
3243 if (!fEndMenu) fRemove = TRUE;
3245 /* finally remove message from the queue */
3247 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3248 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3249 else mt.trackFlags &= ~TF_SKIPREMOVE;
3251 __FINALLY( release_capture )
3253 /* If dropdown is still painted and the close box is clicked on
3254 then the menu will be destroyed as part of the DispatchMessage above.
3255 This will then invalidate the menu handle in mt.hTopMenu. We should
3256 check for this first. */
3257 if( IsMenu( mt.hTopMenu ) )
3259 menu = MENU_GetMenu( mt.hTopMenu );
3261 if( IsWindow( mt.hOwnerWnd ) )
3263 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3265 if (menu && (menu->wFlags & MF_POPUP))
3267 DestroyWindow( menu->hWnd );
3268 menu->hWnd = 0;
3270 if (!(wFlags & TPM_NONOTIFY))
3271 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3272 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3274 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3275 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3278 /* Reset the variable for hiding menu */
3279 if( menu ) menu->bTimeToHide = FALSE;
3282 /* The return value is only used by TrackPopupMenu */
3283 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3284 if (executedMenuId == -1) executedMenuId = 0;
3285 return executedMenuId;
3288 /***********************************************************************
3289 * MENU_InitTracking
3291 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3293 POPUPMENU *menu;
3295 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3297 HideCaret(0);
3299 /* This makes the menus of applications built with Delphi work.
3300 * It also enables menus to be displayed in more than one window,
3301 * but there are some bugs left that need to be fixed in this case.
3303 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3305 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3306 if (!(wFlags & TPM_NONOTIFY))
3307 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3309 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3311 if (!(wFlags & TPM_NONOTIFY))
3313 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3314 /* If an app changed/recreated menu bar entries in WM_INITMENU
3315 * menu sizes will be recalculated once the menu created/shown.
3319 return TRUE;
3322 /***********************************************************************
3323 * MENU_ExitTracking
3325 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3327 TRACE("hwnd=%p\n", hWnd);
3329 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3330 ShowCaret(0);
3331 top_popup = 0;
3332 top_popup_hmenu = NULL;
3333 return TRUE;
3336 /***********************************************************************
3337 * MENU_TrackMouseMenuBar
3339 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3341 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3343 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3344 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3346 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3348 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3349 if (IsMenu(hMenu))
3351 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3352 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3353 MENU_ExitTracking(hWnd, FALSE);
3358 /***********************************************************************
3359 * MENU_TrackKbdMenuBar
3361 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3363 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3365 UINT uItem = NO_SELECTED_ITEM;
3366 HMENU hTrackMenu;
3367 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3369 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3371 /* find window that has a menu */
3373 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3374 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3376 /* check if we have to track a system menu */
3378 hTrackMenu = GetMenu( hwnd );
3379 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3381 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3382 hTrackMenu = get_win_sys_menu( hwnd );
3383 uItem = 0;
3384 wParam |= HTSYSMENU; /* prevent item lookup */
3386 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3388 if (!IsMenu( hTrackMenu )) return;
3390 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3392 if( wChar && wChar != ' ' )
3394 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3395 if ( uItem >= (UINT)(-2) )
3397 if( uItem == (UINT)(-1) ) MessageBeep(0);
3398 /* schedule end of menu tracking */
3399 wFlags |= TF_ENDMENU;
3400 goto track_menu;
3404 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3406 if (!(wParam & HTSYSMENU) || wChar == ' ')
3408 if( uItem == NO_SELECTED_ITEM )
3409 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3410 else
3411 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3414 track_menu:
3415 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3416 MENU_ExitTracking( hwnd, FALSE );
3419 /**********************************************************************
3420 * TrackPopupMenuEx (USER32.@)
3422 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3423 HWND hWnd, LPTPMPARAMS lpTpm )
3425 POPUPMENU *menu;
3426 BOOL ret = FALSE;
3428 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3429 hMenu, wFlags, x, y, hWnd, lpTpm,
3430 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3432 /* Parameter check */
3433 /* FIXME: this check is performed several times, here and in the called
3434 functions. That could be optimized */
3435 if (!(menu = MENU_GetMenu( hMenu )))
3437 SetLastError( ERROR_INVALID_MENU_HANDLE );
3438 return FALSE;
3441 if (IsWindow(menu->hWnd))
3443 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3444 return FALSE;
3447 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3449 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3450 if (!(wFlags & TPM_NONOTIFY))
3451 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3453 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3454 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3455 lpTpm ? &lpTpm->rcExclude : NULL );
3456 MENU_ExitTracking(hWnd, TRUE);
3458 return ret;
3461 /**********************************************************************
3462 * TrackPopupMenu (USER32.@)
3464 * Like the win32 API, the function return the command ID only if the
3465 * flag TPM_RETURNCMD is on.
3468 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3469 INT nReserved, HWND hWnd, const RECT *lpRect )
3471 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3474 /***********************************************************************
3475 * PopupMenuWndProc
3477 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3479 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3481 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3483 switch(message)
3485 case WM_CREATE:
3487 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3488 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3489 return 0;
3492 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3493 return MA_NOACTIVATE;
3495 case WM_PAINT:
3497 PAINTSTRUCT ps;
3498 BeginPaint( hwnd, &ps );
3499 MENU_DrawPopupMenu( hwnd, ps.hdc,
3500 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3501 EndPaint( hwnd, &ps );
3502 return 0;
3505 case WM_PRINTCLIENT:
3507 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3508 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3509 return 0;
3512 case WM_ERASEBKGND:
3513 return 1;
3515 case WM_DESTROY:
3516 /* zero out global pointer in case resident popup window was destroyed. */
3517 if (hwnd == top_popup) {
3518 top_popup = 0;
3519 top_popup_hmenu = NULL;
3521 break;
3523 case WM_SHOWWINDOW:
3525 if( wParam )
3527 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3529 else
3530 SetWindowLongPtrW( hwnd, 0, 0 );
3531 break;
3533 case MM_SETMENUHANDLE:
3534 SetWindowLongPtrW( hwnd, 0, wParam );
3535 break;
3537 case MM_GETMENUHANDLE:
3538 case MN_GETHMENU:
3539 return GetWindowLongPtrW( hwnd, 0 );
3541 default:
3542 return DefWindowProcW( hwnd, message, wParam, lParam );
3544 return 0;
3548 /***********************************************************************
3549 * MENU_GetMenuBarHeight
3551 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3553 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3554 INT orgX, INT orgY )
3556 HDC hdc;
3557 RECT rectBar;
3558 LPPOPUPMENU lppop;
3560 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3562 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3564 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3565 SelectObject( hdc, get_menu_font(FALSE));
3566 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3567 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3568 ReleaseDC( hwnd, hdc );
3569 return lppop->Height;
3573 /*******************************************************************
3574 * ChangeMenuA (USER32.@)
3576 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3577 UINT id, UINT flags )
3579 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3580 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3581 id, data );
3582 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3583 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3584 id, data );
3585 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3586 flags & MF_BYPOSITION ? pos : id,
3587 flags & ~MF_REMOVE );
3588 /* Default: MF_INSERT */
3589 return InsertMenuA( hMenu, pos, flags, id, data );
3593 /*******************************************************************
3594 * ChangeMenuW (USER32.@)
3596 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3597 UINT id, UINT flags )
3599 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3600 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3601 id, data );
3602 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3603 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3604 id, data );
3605 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3606 flags & MF_BYPOSITION ? pos : id,
3607 flags & ~MF_REMOVE );
3608 /* Default: MF_INSERT */
3609 return InsertMenuW( hMenu, pos, flags, id, data );
3613 /*******************************************************************
3614 * CheckMenuItem (USER32.@)
3616 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3618 MENUITEM *item;
3619 DWORD ret;
3621 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3622 ret = item->fState & MF_CHECKED;
3623 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3624 else item->fState &= ~MF_CHECKED;
3625 return ret;
3629 /**********************************************************************
3630 * EnableMenuItem (USER32.@)
3632 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3634 UINT oldflags;
3635 MENUITEM *item;
3636 POPUPMENU *menu;
3638 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3640 /* Get the Popupmenu to access the owner menu */
3641 if (!(menu = MENU_GetMenu(hMenu)))
3642 return (UINT)-1;
3644 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3645 return (UINT)-1;
3647 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3648 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3650 /* If the close item in the system menu change update the close button */
3651 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3653 if (menu->hSysMenuOwner != 0)
3655 RECT rc;
3656 POPUPMENU* parentMenu;
3658 /* Get the parent menu to access*/
3659 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3660 return (UINT)-1;
3662 /* Refresh the frame to reflect the change */
3663 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3664 rc.bottom = 0;
3665 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3669 return oldflags;
3673 /*******************************************************************
3674 * GetMenuStringA (USER32.@)
3676 INT WINAPI GetMenuStringA(
3677 HMENU hMenu, /* [in] menuhandle */
3678 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3679 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3680 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3681 UINT wFlags /* [in] MF_ flags */
3683 MENUITEM *item;
3685 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3686 if (str && nMaxSiz) str[0] = '\0';
3687 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3688 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3689 return 0;
3691 if (!item->text) return 0;
3692 if (!str || !nMaxSiz) return strlenW(item->text);
3693 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3694 str[nMaxSiz-1] = 0;
3695 TRACE("returning %s\n", debugstr_a(str));
3696 return strlen(str);
3700 /*******************************************************************
3701 * GetMenuStringW (USER32.@)
3703 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3704 LPWSTR str, INT nMaxSiz, UINT wFlags )
3706 MENUITEM *item;
3708 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3709 if (str && nMaxSiz) str[0] = '\0';
3710 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3711 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3712 return 0;
3714 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3715 if( !(item->text)) {
3716 str[0] = 0;
3717 return 0;
3719 lstrcpynW( str, item->text, nMaxSiz );
3720 TRACE("returning %s\n", debugstr_w(str));
3721 return strlenW(str);
3725 /**********************************************************************
3726 * HiliteMenuItem (USER32.@)
3728 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3729 UINT wHilite )
3731 LPPOPUPMENU menu;
3732 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3733 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3734 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3735 if (menu->FocusedItem == wItemID) return TRUE;
3736 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3737 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3738 return TRUE;
3742 /**********************************************************************
3743 * GetMenuState (USER32.@)
3745 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3747 MENUITEM *item;
3748 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3749 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3750 debug_print_menuitem (" item: ", item, "");
3751 if (item->fType & MF_POPUP)
3753 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3754 if (!menu) return -1;
3755 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3757 else
3759 /* We used to (from way back then) mask the result to 0xff. */
3760 /* I don't know why and it seems wrong as the documented */
3761 /* return flag MF_SEPARATOR is outside that mask. */
3762 return (item->fType | item->fState);
3767 /**********************************************************************
3768 * GetMenuItemCount (USER32.@)
3770 INT WINAPI GetMenuItemCount( HMENU hMenu )
3772 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3773 if (!menu) return -1;
3774 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3775 return menu->nItems;
3779 /**********************************************************************
3780 * GetMenuItemID (USER32.@)
3782 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3784 MENUITEM * lpmi;
3786 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3787 if (lpmi->fType & MF_POPUP) return -1;
3788 return lpmi->wID;
3793 /**********************************************************************
3794 * MENU_mnu2mnuii
3796 * Uses flags, id and text ptr, passed by InsertMenu() and
3797 * ModifyMenu() to setup a MenuItemInfo structure.
3799 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3800 LPMENUITEMINFOW pmii)
3802 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3803 pmii->cbSize = sizeof( MENUITEMINFOW);
3804 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3805 /* setting bitmap clears text and vice versa */
3806 if( IS_STRING_ITEM(flags)) {
3807 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3808 if( !str)
3809 flags |= MF_SEPARATOR;
3810 /* Item beginning with a backspace is a help item */
3811 /* FIXME: wrong place, this is only true in win16 */
3812 else if( *str == '\b') {
3813 flags |= MF_HELP;
3814 str++;
3816 pmii->dwTypeData = (LPWSTR)str;
3817 } else if( flags & MFT_BITMAP){
3818 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3819 pmii->hbmpItem = (HBITMAP)str;
3821 if( flags & MF_OWNERDRAW){
3822 pmii->fMask |= MIIM_DATA;
3823 pmii->dwItemData = (ULONG_PTR) str;
3825 if( flags & MF_POPUP) {
3826 pmii->fMask |= MIIM_SUBMENU;
3827 pmii->hSubMenu = (HMENU)id;
3829 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3830 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3831 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3832 pmii->wID = (UINT)id;
3836 /*******************************************************************
3837 * InsertMenuW (USER32.@)
3839 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3840 UINT_PTR id, LPCWSTR str )
3842 MENUITEM *item;
3843 MENUITEMINFOW mii;
3845 if (IS_STRING_ITEM(flags) && str)
3846 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3847 hMenu, pos, flags, id, debugstr_w(str) );
3848 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3849 hMenu, pos, flags, id, str );
3851 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3852 MENU_mnu2mnuii( flags, id, str, &mii);
3853 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3855 RemoveMenu( hMenu, pos, flags );
3856 return FALSE;
3859 item->hCheckBit = item->hUnCheckBit = 0;
3860 return TRUE;
3864 /*******************************************************************
3865 * InsertMenuA (USER32.@)
3867 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3868 UINT_PTR id, LPCSTR str )
3870 BOOL ret = FALSE;
3872 if (IS_STRING_ITEM(flags) && str)
3874 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3875 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3876 if (newstr)
3878 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3879 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3880 HeapFree( GetProcessHeap(), 0, newstr );
3882 return ret;
3884 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3888 /*******************************************************************
3889 * AppendMenuA (USER32.@)
3891 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3892 UINT_PTR id, LPCSTR data )
3894 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3898 /*******************************************************************
3899 * AppendMenuW (USER32.@)
3901 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3902 UINT_PTR id, LPCWSTR data )
3904 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3908 /**********************************************************************
3909 * RemoveMenu (USER32.@)
3911 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3913 LPPOPUPMENU menu;
3914 MENUITEM *item;
3916 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3917 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3918 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3920 /* Remove item */
3922 MENU_FreeItemData( item );
3924 if (--menu->nItems == 0)
3926 HeapFree( GetProcessHeap(), 0, menu->items );
3927 menu->items = NULL;
3929 else
3931 while(nPos < menu->nItems)
3933 *item = *(item+1);
3934 item++;
3935 nPos++;
3937 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3938 menu->nItems * sizeof(MENUITEM) );
3940 return TRUE;
3944 /**********************************************************************
3945 * DeleteMenu (USER32.@)
3947 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3949 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3950 if (!item) return FALSE;
3951 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3952 /* nPos is now the position of the item */
3953 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3954 return TRUE;
3958 /*******************************************************************
3959 * ModifyMenuW (USER32.@)
3961 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3962 UINT_PTR id, LPCWSTR str )
3964 MENUITEM *item;
3965 MENUITEMINFOW mii;
3967 if (IS_STRING_ITEM(flags))
3968 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3969 else
3970 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3972 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3973 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3974 MENU_mnu2mnuii( flags, id, str, &mii);
3975 return SetMenuItemInfo_common( item, &mii, TRUE);
3979 /*******************************************************************
3980 * ModifyMenuA (USER32.@)
3982 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3983 UINT_PTR id, LPCSTR str )
3985 BOOL ret = FALSE;
3987 if (IS_STRING_ITEM(flags) && str)
3989 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3990 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3991 if (newstr)
3993 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3994 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3995 HeapFree( GetProcessHeap(), 0, newstr );
3997 return ret;
3999 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4003 /**********************************************************************
4004 * CreatePopupMenu (USER32.@)
4006 HMENU WINAPI CreatePopupMenu(void)
4008 HMENU hmenu;
4009 POPUPMENU *menu;
4011 if (!(hmenu = CreateMenu())) return 0;
4012 menu = MENU_GetMenu( hmenu );
4013 menu->wFlags |= MF_POPUP;
4014 menu->bTimeToHide = FALSE;
4015 return hmenu;
4019 /**********************************************************************
4020 * GetMenuCheckMarkDimensions (USER.417)
4021 * GetMenuCheckMarkDimensions (USER32.@)
4023 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4025 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4029 /**********************************************************************
4030 * SetMenuItemBitmaps (USER32.@)
4032 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4033 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4035 MENUITEM *item;
4037 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4039 if (!hNewCheck && !hNewUnCheck)
4041 item->fState &= ~MF_USECHECKBITMAPS;
4043 else /* Install new bitmaps */
4045 item->hCheckBit = hNewCheck;
4046 item->hUnCheckBit = hNewUnCheck;
4047 item->fState |= MF_USECHECKBITMAPS;
4049 return TRUE;
4053 /**********************************************************************
4054 * CreateMenu (USER32.@)
4056 HMENU WINAPI CreateMenu(void)
4058 HMENU hMenu;
4059 LPPOPUPMENU menu;
4061 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4062 menu->FocusedItem = NO_SELECTED_ITEM;
4063 menu->bTimeToHide = FALSE;
4065 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4067 TRACE("return %p\n", hMenu );
4069 return hMenu;
4073 /**********************************************************************
4074 * DestroyMenu (USER32.@)
4076 BOOL WINAPI DestroyMenu( HMENU hMenu )
4078 LPPOPUPMENU lppop;
4080 TRACE("(%p)\n", hMenu);
4082 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4083 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4085 /* DestroyMenu should not destroy system menu popup owner */
4086 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4088 DestroyWindow( lppop->hWnd );
4089 lppop->hWnd = 0;
4092 if (lppop->items) /* recursively destroy submenus */
4094 int i;
4095 MENUITEM *item = lppop->items;
4096 for (i = lppop->nItems; i > 0; i--, item++)
4098 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4099 MENU_FreeItemData( item );
4101 HeapFree( GetProcessHeap(), 0, lppop->items );
4103 HeapFree( GetProcessHeap(), 0, lppop );
4104 return TRUE;
4108 /**********************************************************************
4109 * GetSystemMenu (USER32.@)
4111 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4113 WND *wndPtr = WIN_GetPtr( hWnd );
4114 HMENU retvalue = 0;
4116 if (wndPtr == WND_DESKTOP) return 0;
4117 if (wndPtr == WND_OTHER_PROCESS)
4119 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4121 else if (wndPtr)
4123 if (wndPtr->hSysMenu && bRevert)
4125 DestroyMenu(wndPtr->hSysMenu);
4126 wndPtr->hSysMenu = 0;
4129 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4130 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4132 if( wndPtr->hSysMenu )
4134 POPUPMENU *menu;
4135 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4137 /* Store the dummy sysmenu handle to facilitate the refresh */
4138 /* of the close button if the SC_CLOSE item change */
4139 menu = MENU_GetMenu(retvalue);
4140 if ( menu )
4141 menu->hSysMenuOwner = wndPtr->hSysMenu;
4143 WIN_ReleasePtr( wndPtr );
4145 return bRevert ? 0 : retvalue;
4149 /*******************************************************************
4150 * SetSystemMenu (USER32.@)
4152 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4154 WND *wndPtr = WIN_GetPtr( hwnd );
4156 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4158 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4159 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4160 WIN_ReleasePtr( wndPtr );
4161 return TRUE;
4163 return FALSE;
4167 /**********************************************************************
4168 * GetMenu (USER32.@)
4170 HMENU WINAPI GetMenu( HWND hWnd )
4172 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4173 TRACE("for %p returning %p\n", hWnd, retvalue);
4174 return retvalue;
4177 /**********************************************************************
4178 * GetMenuBarInfo (USER32.@)
4180 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4182 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4183 return FALSE;
4186 /**********************************************************************
4187 * MENU_SetMenu
4189 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4190 * SetWindowPos call that would result if SetMenu were called directly.
4192 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4194 TRACE("(%p, %p);\n", hWnd, hMenu);
4196 if (hMenu && !IsMenu(hMenu))
4198 WARN("hMenu %p is not a menu handle\n", hMenu);
4199 return FALSE;
4201 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4202 return FALSE;
4204 hWnd = WIN_GetFullHandle( hWnd );
4205 if (GetCapture() == hWnd)
4206 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4208 if (hMenu != 0)
4210 LPPOPUPMENU lpmenu;
4212 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4214 lpmenu->hWnd = hWnd;
4215 lpmenu->Height = 0; /* Make sure we recalculate the size */
4217 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4218 return TRUE;
4222 /**********************************************************************
4223 * SetMenu (USER32.@)
4225 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4227 if(!MENU_SetMenu(hWnd, hMenu))
4228 return FALSE;
4230 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4231 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4232 return TRUE;
4236 /**********************************************************************
4237 * GetSubMenu (USER32.@)
4239 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4241 MENUITEM * lpmi;
4243 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4244 if (!(lpmi->fType & MF_POPUP)) return 0;
4245 return lpmi->hSubMenu;
4249 /**********************************************************************
4250 * DrawMenuBar (USER32.@)
4252 BOOL WINAPI DrawMenuBar( HWND hWnd )
4254 LPPOPUPMENU lppop;
4255 HMENU hMenu = GetMenu(hWnd);
4257 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4258 return FALSE;
4259 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4261 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4262 lppop->hwndOwner = hWnd;
4263 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4264 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4265 return TRUE;
4268 /***********************************************************************
4269 * DrawMenuBarTemp (USER32.@)
4271 * UNDOCUMENTED !!
4273 * called by W98SE desk.cpl Control Panel Applet
4275 * Not 100% sure about the param names, but close.
4277 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4279 LPPOPUPMENU lppop;
4280 UINT i,retvalue;
4281 HFONT hfontOld = 0;
4282 BOOL flat_menu = FALSE;
4284 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4286 if (!hMenu)
4287 hMenu = GetMenu(hwnd);
4289 if (!hFont)
4290 hFont = get_menu_font(FALSE);
4292 lppop = MENU_GetMenu( hMenu );
4293 if (lppop == NULL || lprect == NULL)
4295 retvalue = GetSystemMetrics(SM_CYMENU);
4296 goto END;
4299 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4301 hfontOld = SelectObject( hDC, hFont);
4303 if (lppop->Height == 0)
4304 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4306 lprect->bottom = lprect->top + lppop->Height;
4308 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4310 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4311 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4312 LineTo( hDC, lprect->right, lprect->bottom );
4314 if (lppop->nItems == 0)
4316 retvalue = GetSystemMetrics(SM_CYMENU);
4317 goto END;
4320 for (i = 0; i < lppop->nItems; i++)
4322 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4323 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4325 retvalue = lppop->Height;
4327 END:
4328 if (hfontOld) SelectObject (hDC, hfontOld);
4329 return retvalue;
4332 /***********************************************************************
4333 * EndMenu (USER.187)
4334 * EndMenu (USER32.@)
4336 BOOL WINAPI EndMenu(void)
4338 /* if we are in the menu code, and it is active */
4339 if (!fEndMenu && top_popup)
4341 /* terminate the menu handling code */
4342 fEndMenu = TRUE;
4344 /* needs to be posted to wakeup the internal menu handler */
4345 /* which will now terminate the menu, in the event that */
4346 /* the main window was minimized, or lost focus, so we */
4347 /* don't end up with an orphaned menu */
4348 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4350 return fEndMenu;
4354 /*****************************************************************
4355 * LoadMenuA (USER32.@)
4357 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4359 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4360 if (!hrsrc) return 0;
4361 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4365 /*****************************************************************
4366 * LoadMenuW (USER32.@)
4368 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4370 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4371 if (!hrsrc) return 0;
4372 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4376 /**********************************************************************
4377 * LoadMenuIndirectW (USER32.@)
4379 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4381 HMENU hMenu;
4382 WORD version, offset;
4383 LPCSTR p = template;
4385 version = GET_WORD(p);
4386 p += sizeof(WORD);
4387 TRACE("%p, ver %d\n", template, version );
4388 switch (version)
4390 case 0: /* standard format is version of 0 */
4391 offset = GET_WORD(p);
4392 p += sizeof(WORD) + offset;
4393 if (!(hMenu = CreateMenu())) return 0;
4394 if (!MENU_ParseResource( p, hMenu ))
4396 DestroyMenu( hMenu );
4397 return 0;
4399 return hMenu;
4400 case 1: /* extended format is version of 1 */
4401 offset = GET_WORD(p);
4402 p += sizeof(WORD) + offset;
4403 if (!(hMenu = CreateMenu())) return 0;
4404 if (!MENUEX_ParseResource( p, hMenu))
4406 DestroyMenu( hMenu );
4407 return 0;
4409 return hMenu;
4410 default:
4411 ERR("version %d not supported.\n", version);
4412 return 0;
4417 /**********************************************************************
4418 * LoadMenuIndirectA (USER32.@)
4420 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4422 return LoadMenuIndirectW( template );
4426 /**********************************************************************
4427 * IsMenu (USER32.@)
4429 BOOL WINAPI IsMenu(HMENU hmenu)
4431 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4433 if (!menu)
4435 SetLastError(ERROR_INVALID_MENU_HANDLE);
4436 return FALSE;
4438 return TRUE;
4441 /**********************************************************************
4442 * GetMenuItemInfo_common
4445 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4446 LPMENUITEMINFOW lpmii, BOOL unicode)
4448 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4450 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4452 if (!menu) {
4453 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4454 return FALSE;
4457 if( lpmii->fMask & MIIM_TYPE) {
4458 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4459 WARN("invalid combination of fMask bits used\n");
4460 /* this does not happen on Win9x/ME */
4461 SetLastError( ERROR_INVALID_PARAMETER);
4462 return FALSE;
4464 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4465 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4466 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4467 if( lpmii->fType & MFT_BITMAP) {
4468 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4469 lpmii->cch = 0;
4470 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4471 /* this does not happen on Win9x/ME */
4472 lpmii->dwTypeData = 0;
4473 lpmii->cch = 0;
4477 /* copy the text string */
4478 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4479 if( !menu->text ) {
4480 if(lpmii->dwTypeData && lpmii->cch) {
4481 lpmii->cch = 0;
4482 if( unicode)
4483 *((WCHAR *)lpmii->dwTypeData) = 0;
4484 else
4485 *((CHAR *)lpmii->dwTypeData) = 0;
4487 } else {
4488 int len;
4489 if (unicode)
4491 len = strlenW(menu->text);
4492 if(lpmii->dwTypeData && lpmii->cch)
4493 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4495 else
4497 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4498 0, NULL, NULL ) - 1;
4499 if(lpmii->dwTypeData && lpmii->cch)
4500 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4501 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4502 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4504 /* if we've copied a substring we return its length */
4505 if(lpmii->dwTypeData && lpmii->cch)
4506 if (lpmii->cch <= len + 1)
4507 lpmii->cch--;
4508 else
4509 lpmii->cch = len;
4510 else {
4511 /* return length of string */
4512 /* not on Win9x/ME if fType & MFT_BITMAP */
4513 lpmii->cch = len;
4518 if (lpmii->fMask & MIIM_FTYPE)
4519 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4521 if (lpmii->fMask & MIIM_BITMAP)
4522 lpmii->hbmpItem = menu->hbmpItem;
4524 if (lpmii->fMask & MIIM_STATE)
4525 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4527 if (lpmii->fMask & MIIM_ID)
4528 lpmii->wID = menu->wID;
4530 if (lpmii->fMask & MIIM_SUBMENU)
4531 lpmii->hSubMenu = menu->hSubMenu;
4532 else {
4533 /* hSubMenu is always cleared
4534 * (not on Win9x/ME ) */
4535 lpmii->hSubMenu = 0;
4538 if (lpmii->fMask & MIIM_CHECKMARKS) {
4539 lpmii->hbmpChecked = menu->hCheckBit;
4540 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4542 if (lpmii->fMask & MIIM_DATA)
4543 lpmii->dwItemData = menu->dwItemData;
4545 return TRUE;
4548 /**********************************************************************
4549 * GetMenuItemInfoA (USER32.@)
4551 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4552 LPMENUITEMINFOA lpmii)
4554 BOOL ret;
4555 MENUITEMINFOA mii;
4556 if( lpmii->cbSize != sizeof( mii) &&
4557 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4558 SetLastError( ERROR_INVALID_PARAMETER);
4559 return FALSE;
4561 memcpy( &mii, lpmii, lpmii->cbSize);
4562 mii.cbSize = sizeof( mii);
4563 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4564 (LPMENUITEMINFOW)&mii, FALSE);
4565 mii.cbSize = lpmii->cbSize;
4566 memcpy( lpmii, &mii, mii.cbSize);
4567 return ret;
4570 /**********************************************************************
4571 * GetMenuItemInfoW (USER32.@)
4573 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4574 LPMENUITEMINFOW lpmii)
4576 BOOL ret;
4577 MENUITEMINFOW mii;
4578 if( lpmii->cbSize != sizeof( mii) &&
4579 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4580 SetLastError( ERROR_INVALID_PARAMETER);
4581 return FALSE;
4583 memcpy( &mii, lpmii, lpmii->cbSize);
4584 mii.cbSize = sizeof( mii);
4585 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4586 mii.cbSize = lpmii->cbSize;
4587 memcpy( lpmii, &mii, mii.cbSize);
4588 return ret;
4592 /* set a menu item text from a ASCII or Unicode string */
4593 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4595 if (!text)
4596 menu->text = NULL;
4597 else if (unicode)
4599 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4600 strcpyW( menu->text, text );
4602 else
4604 LPCSTR str = (LPCSTR)text;
4605 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4606 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4607 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4612 /**********************************************************************
4613 * MENU_depth
4615 * detect if there are loops in the menu tree (or the depth is too large)
4617 static int MENU_depth( POPUPMENU *pmenu, int depth)
4619 int i;
4620 MENUITEM *item;
4621 int subdepth;
4623 depth++;
4624 if( depth > MAXMENUDEPTH) return depth;
4625 item = pmenu->items;
4626 subdepth = depth;
4627 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4628 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4629 if( psubmenu){
4630 int bdepth = MENU_depth( psubmenu, depth);
4631 if( bdepth > subdepth) subdepth = bdepth;
4633 if( subdepth > MAXMENUDEPTH)
4634 TRACE("<- hmenu %p\n", item->hSubMenu);
4636 return subdepth;
4640 /**********************************************************************
4641 * SetMenuItemInfo_common
4643 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4644 * MIIM_BITMAP and MIIM_STRING flags instead.
4647 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4648 const MENUITEMINFOW *lpmii,
4649 BOOL unicode)
4651 if (!menu) return FALSE;
4653 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4655 if (lpmii->fMask & MIIM_FTYPE ) {
4656 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4657 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4659 if (lpmii->fMask & MIIM_STRING ) {
4660 /* free the string when used */
4661 HeapFree(GetProcessHeap(), 0, menu->text);
4662 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4665 if (lpmii->fMask & MIIM_STATE)
4666 /* Other menu items having MFS_DEFAULT are not converted
4667 to normal items */
4668 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4670 if (lpmii->fMask & MIIM_ID)
4671 menu->wID = lpmii->wID;
4673 if (lpmii->fMask & MIIM_SUBMENU) {
4674 menu->hSubMenu = lpmii->hSubMenu;
4675 if (menu->hSubMenu) {
4676 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4677 if (subMenu) {
4678 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4679 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4680 menu->hSubMenu = 0;
4681 return FALSE;
4683 subMenu->wFlags |= MF_POPUP;
4684 menu->fType |= MF_POPUP;
4685 } else {
4686 SetLastError( ERROR_INVALID_PARAMETER);
4687 return FALSE;
4690 else
4691 menu->fType &= ~MF_POPUP;
4694 if (lpmii->fMask & MIIM_CHECKMARKS)
4696 menu->hCheckBit = lpmii->hbmpChecked;
4697 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4699 if (lpmii->fMask & MIIM_DATA)
4700 menu->dwItemData = lpmii->dwItemData;
4702 if (lpmii->fMask & MIIM_BITMAP)
4703 menu->hbmpItem = lpmii->hbmpItem;
4705 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4706 menu->fType |= MFT_SEPARATOR;
4708 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4709 return TRUE;
4712 /**********************************************************************
4713 * MENU_NormalizeMenuItemInfoStruct
4715 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4716 * check, copy and extend the MENUITEMINFO struct from the version that the application
4717 * supplied to the version used by wine source. */
4718 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4719 MENUITEMINFOW *pmii_out )
4721 /* do we recognize the size? */
4722 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4723 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4724 SetLastError( ERROR_INVALID_PARAMETER);
4725 return FALSE;
4727 /* copy the fields that we have */
4728 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4729 /* if the hbmpItem member is missing then extend */
4730 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4731 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4732 pmii_out->hbmpItem = NULL;
4734 /* test for invalid bit combinations */
4735 if( (pmii_out->fMask & MIIM_TYPE &&
4736 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4737 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4738 WARN("invalid combination of fMask bits used\n");
4739 /* this does not happen on Win9x/ME */
4740 SetLastError( ERROR_INVALID_PARAMETER);
4741 return FALSE;
4743 /* convert old style (MIIM_TYPE) to the new */
4744 if( pmii_out->fMask & MIIM_TYPE){
4745 pmii_out->fMask |= MIIM_FTYPE;
4746 if( IS_STRING_ITEM(pmii_out->fType)){
4747 pmii_out->fMask |= MIIM_STRING;
4748 } else if( (pmii_out->fType) & MFT_BITMAP){
4749 pmii_out->fMask |= MIIM_BITMAP;
4750 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4753 return TRUE;
4756 /**********************************************************************
4757 * SetMenuItemInfoA (USER32.@)
4759 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4760 const MENUITEMINFOA *lpmii)
4762 MENUITEMINFOW mii;
4764 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4766 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4768 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4769 &mii, FALSE);
4772 /**********************************************************************
4773 * SetMenuItemInfoW (USER32.@)
4775 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4776 const MENUITEMINFOW *lpmii)
4778 MENUITEMINFOW mii;
4780 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4782 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4783 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4784 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4787 /**********************************************************************
4788 * SetMenuDefaultItem (USER32.@)
4791 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4793 UINT i;
4794 POPUPMENU *menu;
4795 MENUITEM *item;
4797 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4799 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4801 /* reset all default-item flags */
4802 item = menu->items;
4803 for (i = 0; i < menu->nItems; i++, item++)
4805 item->fState &= ~MFS_DEFAULT;
4808 /* no default item */
4809 if ( -1 == uItem)
4811 return TRUE;
4814 item = menu->items;
4815 if ( bypos )
4817 if ( uItem >= menu->nItems ) return FALSE;
4818 item[uItem].fState |= MFS_DEFAULT;
4819 return TRUE;
4821 else
4823 for (i = 0; i < menu->nItems; i++, item++)
4825 if (item->wID == uItem)
4827 item->fState |= MFS_DEFAULT;
4828 return TRUE;
4833 return FALSE;
4836 /**********************************************************************
4837 * GetMenuDefaultItem (USER32.@)
4839 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4841 POPUPMENU *menu;
4842 MENUITEM * item;
4843 UINT i = 0;
4845 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4847 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4849 /* find default item */
4850 item = menu->items;
4852 /* empty menu */
4853 if (! item) return -1;
4855 while ( !( item->fState & MFS_DEFAULT ) )
4857 i++; item++;
4858 if (i >= menu->nItems ) return -1;
4861 /* default: don't return disabled items */
4862 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4864 /* search rekursiv when needed */
4865 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4867 UINT ret;
4868 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4869 if ( -1 != ret ) return ret;
4871 /* when item not found in submenu, return the popup item */
4873 return ( bypos ) ? i : item->wID;
4878 /**********************************************************************
4879 * InsertMenuItemA (USER32.@)
4881 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4882 const MENUITEMINFOA *lpmii)
4884 MENUITEM *item;
4885 MENUITEMINFOW mii;
4887 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4889 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4891 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4892 return SetMenuItemInfo_common(item, &mii, FALSE);
4896 /**********************************************************************
4897 * InsertMenuItemW (USER32.@)
4899 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4900 const MENUITEMINFOW *lpmii)
4902 MENUITEM *item;
4903 MENUITEMINFOW mii;
4905 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4907 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4909 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4910 return SetMenuItemInfo_common(item, &mii, TRUE);
4913 /**********************************************************************
4914 * CheckMenuRadioItem (USER32.@)
4917 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4918 UINT first, UINT last, UINT check,
4919 UINT bypos)
4921 BOOL done = FALSE;
4922 UINT i;
4923 MENUITEM *mi_first = NULL, *mi_check;
4924 HMENU m_first, m_check;
4926 for (i = first; i <= last; i++)
4928 UINT pos = i;
4930 if (!mi_first)
4932 m_first = hMenu;
4933 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4934 if (!mi_first) continue;
4935 mi_check = mi_first;
4936 m_check = m_first;
4938 else
4940 m_check = hMenu;
4941 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4942 if (!mi_check) continue;
4945 if (m_first != m_check) continue;
4946 if (mi_check->fType == MFT_SEPARATOR) continue;
4948 if (i == check)
4950 mi_check->fType |= MFT_RADIOCHECK;
4951 mi_check->fState |= MFS_CHECKED;
4952 done = TRUE;
4954 else
4956 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4957 mi_check->fState &= ~MFS_CHECKED;
4961 return done;
4965 /**********************************************************************
4966 * GetMenuItemRect (USER32.@)
4968 * ATTENTION: Here, the returned values in rect are the screen
4969 * coordinates of the item just like if the menu was
4970 * always on the upper left side of the application.
4973 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4974 LPRECT rect)
4976 POPUPMENU *itemMenu;
4977 MENUITEM *item;
4978 HWND referenceHwnd;
4980 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4982 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4983 referenceHwnd = hwnd;
4985 if(!hwnd)
4987 itemMenu = MENU_GetMenu(hMenu);
4988 if (itemMenu == NULL)
4989 return FALSE;
4991 if(itemMenu->hWnd == 0)
4992 return FALSE;
4993 referenceHwnd = itemMenu->hWnd;
4996 if ((rect == NULL) || (item == NULL))
4997 return FALSE;
4999 *rect = item->rect;
5001 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5003 return TRUE;
5006 /**********************************************************************
5007 * SetMenuInfo (USER32.@)
5009 * FIXME
5010 * actually use the items to draw the menu
5011 * (recalculate and/or redraw)
5013 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5015 POPUPMENU *menu;
5016 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5018 if (lpmi->fMask & MIM_BACKGROUND)
5019 menu->hbrBack = lpmi->hbrBack;
5021 if (lpmi->fMask & MIM_HELPID)
5022 menu->dwContextHelpID = lpmi->dwContextHelpID;
5024 if (lpmi->fMask & MIM_MAXHEIGHT)
5025 menu->cyMax = lpmi->cyMax;
5027 if (lpmi->fMask & MIM_MENUDATA)
5028 menu->dwMenuData = lpmi->dwMenuData;
5030 if (lpmi->fMask & MIM_STYLE)
5031 menu->dwStyle = lpmi->dwStyle;
5033 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5034 int i;
5035 MENUITEM *item = menu->items;
5036 for( i = menu->nItems; i; i--, item++)
5037 if( item->fType & MF_POPUP)
5038 menu_SetMenuInfo( item->hSubMenu, lpmi);
5040 return TRUE;
5043 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5045 TRACE("(%p %p)\n", hMenu, lpmi);
5046 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5047 if( lpmi->fMask & MIM_STYLE) {
5048 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5049 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5050 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5052 return TRUE;
5054 SetLastError( ERROR_INVALID_PARAMETER);
5055 return FALSE;
5058 /**********************************************************************
5059 * GetMenuInfo (USER32.@)
5061 * NOTES
5062 * win98/NT5.0
5065 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5066 { POPUPMENU *menu;
5068 TRACE("(%p %p)\n", hMenu, lpmi);
5070 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5073 if (lpmi->fMask & MIM_BACKGROUND)
5074 lpmi->hbrBack = menu->hbrBack;
5076 if (lpmi->fMask & MIM_HELPID)
5077 lpmi->dwContextHelpID = menu->dwContextHelpID;
5079 if (lpmi->fMask & MIM_MAXHEIGHT)
5080 lpmi->cyMax = menu->cyMax;
5082 if (lpmi->fMask & MIM_MENUDATA)
5083 lpmi->dwMenuData = menu->dwMenuData;
5085 if (lpmi->fMask & MIM_STYLE)
5086 lpmi->dwStyle = menu->dwStyle;
5088 return TRUE;
5090 SetLastError( ERROR_INVALID_PARAMETER);
5091 return FALSE;
5095 /**********************************************************************
5096 * SetMenuContextHelpId (USER32.@)
5098 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5100 LPPOPUPMENU menu;
5102 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5104 if ((menu = MENU_GetMenu(hMenu)))
5106 menu->dwContextHelpID = dwContextHelpID;
5107 return TRUE;
5109 return FALSE;
5113 /**********************************************************************
5114 * GetMenuContextHelpId (USER32.@)
5116 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5118 LPPOPUPMENU menu;
5120 TRACE("(%p)\n", hMenu);
5122 if ((menu = MENU_GetMenu(hMenu)))
5124 return menu->dwContextHelpID;
5126 return 0;
5129 /**********************************************************************
5130 * MenuItemFromPoint (USER32.@)
5132 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5134 POPUPMENU *menu = MENU_GetMenu(hMenu);
5135 UINT pos;
5137 /*FIXME: Do we have to handle hWnd here? */
5138 if (!menu) return -1;
5139 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5140 return pos;
5144 /**********************************************************************
5145 * translate_accelerator
5147 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5148 BYTE fVirt, WORD key, WORD cmd )
5150 INT mask = 0;
5151 UINT mesg = 0;
5153 if (wParam != key) return FALSE;
5155 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5156 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5157 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5159 if (message == WM_CHAR || message == WM_SYSCHAR)
5161 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5163 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5164 goto found;
5167 else
5169 if(fVirt & FVIRTKEY)
5171 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5172 wParam, 0xff & HIWORD(lParam));
5174 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5175 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5177 else
5179 if (!(lParam & 0x01000000)) /* no special_key */
5181 if ((fVirt & FALT) && (lParam & 0x20000000))
5182 { /* ^^ ALT pressed */
5183 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5184 goto found;
5189 return FALSE;
5191 found:
5192 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5193 mesg = 1;
5194 else
5196 HMENU hMenu, hSubMenu, hSysMenu;
5197 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5199 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5200 hSysMenu = get_win_sys_menu( hWnd );
5202 /* find menu item and ask application to initialize it */
5203 /* 1. in the system menu */
5204 hSubMenu = hSysMenu;
5205 nPos = cmd;
5206 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5208 if (GetCapture())
5209 mesg = 2;
5210 if (!IsWindowEnabled(hWnd))
5211 mesg = 3;
5212 else
5214 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5215 if(hSubMenu != hSysMenu)
5217 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5218 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5219 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5221 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5224 else /* 2. in the window's menu */
5226 hSubMenu = hMenu;
5227 nPos = cmd;
5228 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5230 if (GetCapture())
5231 mesg = 2;
5232 if (!IsWindowEnabled(hWnd))
5233 mesg = 3;
5234 else
5236 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5237 if(hSubMenu != hMenu)
5239 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5240 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5241 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5243 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5248 if (mesg == 0)
5250 if (uSysStat != (UINT)-1)
5252 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5253 mesg=4;
5254 else
5255 mesg=WM_SYSCOMMAND;
5257 else
5259 if (uStat != (UINT)-1)
5261 if (IsIconic(hWnd))
5262 mesg=5;
5263 else
5265 if (uStat & (MF_DISABLED|MF_GRAYED))
5266 mesg=6;
5267 else
5268 mesg=WM_COMMAND;
5271 else
5272 mesg=WM_COMMAND;
5277 if( mesg==WM_COMMAND )
5279 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5280 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5282 else if( mesg==WM_SYSCOMMAND )
5284 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5285 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5287 else
5289 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5290 * #0: unknown (please report!)
5291 * #1: for WM_KEYUP,WM_SYSKEYUP
5292 * #2: mouse is captured
5293 * #3: window is disabled
5294 * #4: it's a disabled system menu option
5295 * #5: it's a menu option, but window is iconic
5296 * #6: it's a menu option, but disabled
5298 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5299 if(mesg==0)
5300 ERR_(accel)(" unknown reason - please report!\n");
5302 return TRUE;
5305 /**********************************************************************
5306 * TranslateAcceleratorA (USER32.@)
5307 * TranslateAccelerator (USER32.@)
5309 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5311 switch (msg->message)
5313 case WM_KEYDOWN:
5314 case WM_SYSKEYDOWN:
5315 return TranslateAcceleratorW( hWnd, hAccel, msg );
5317 case WM_CHAR:
5318 case WM_SYSCHAR:
5320 MSG msgW = *msg;
5321 char ch = LOWORD(msg->wParam);
5322 WCHAR wch;
5323 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5324 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5325 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5328 default:
5329 return 0;
5333 /**********************************************************************
5334 * TranslateAcceleratorW (USER32.@)
5336 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5338 ACCEL data[32], *ptr = data;
5339 int i, count;
5341 if (!hWnd) return 0;
5343 if (msg->message != WM_KEYDOWN &&
5344 msg->message != WM_SYSKEYDOWN &&
5345 msg->message != WM_CHAR &&
5346 msg->message != WM_SYSCHAR)
5347 return 0;
5349 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5350 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5352 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5353 if (count > sizeof(data)/sizeof(data[0]))
5355 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5357 count = CopyAcceleratorTableW( hAccel, ptr, count );
5358 for (i = 0; i < count; i++)
5360 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5361 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5362 break;
5364 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5365 return (i < count);