shell32: Move the IQueryAssociations implementation from shlwapi to shell32.
[wine/multimedia.git] / dlls / user32 / menu.c
bloba3accaacceed112277c07593e6e70a2b9d4ff53d
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #define OEMRESOURCE
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
60 #include "win.h"
61 #include "controls.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu);
66 WINE_DECLARE_DEBUG_CHANNEL(accel);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
74 typedef struct {
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType; /* Item type. */
77 UINT fState; /* Item state. */
78 UINT_PTR wID; /* Item id. */
79 HMENU hSubMenu; /* Pop-up menu. */
80 HBITMAP hCheckBit; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
82 LPWSTR text; /* Item text. */
83 ULONG_PTR dwItemData; /* Application defined. */
84 LPWSTR dwTypeData; /* depends on fMask */
85 HBITMAP hbmpItem; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect; /* Item area (relative to menu window) */
88 UINT xTab; /* X position of text after Tab */
89 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
90 * bitmap */
91 } MENUITEM;
93 /* Popup menu structure */
94 typedef struct {
95 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic; /* Magic number */
97 WORD Width; /* Width of the whole menu */
98 WORD Height; /* Height of the whole menu */
99 UINT nItems; /* Number of items in the menu */
100 HWND hWnd; /* Window containing the menu */
101 MENUITEM *items; /* Array of menu items */
102 UINT FocusedItem; /* Currently focused item */
103 HWND hwndOwner; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling; /* Scroll arrows are active */
106 UINT nScrollPos; /* Current scroll position */
107 UINT nTotalHeight; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle; /* Extended menu style */
110 UINT cyMax; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack; /* brush for menu background */
112 DWORD dwContextHelpID;
113 DWORD dwMenuData; /* application defined value */
114 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
115 WORD textOffset; /* Offset of text when items have both bitmaps and text */
116 } POPUPMENU, *LPPOPUPMENU;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
124 typedef struct
126 UINT trackFlags;
127 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu; /* initial menu */
129 HWND hOwnerWnd; /* where notifications are sent */
130 POINT pt;
131 } MTRACKER;
133 #define MENU_MAGIC 0x554d /* 'MU' */
135 #define ITEM_PREV -1
136 #define ITEM_NEXT 1
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* maximum allowed depth of any branch in the menu tree.
151 * This value is slightly larger than in windows (25) to
152 * stay on the safe side. */
153 #define MAXMENUDEPTH 30
155 /* (other menu->FocusedItem values give the position of the focused item) */
156 #define NO_SELECTED_ITEM 0xffff
158 #define MENU_ITEM_TYPE(flags) \
159 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
161 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
162 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
163 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
165 #define IS_SYSTEM_MENU(menu) \
166 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
168 #define MENUITEMINFO_TYPE_MASK \
169 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
170 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
171 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
172 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
173 #define STATE_MASK (~TYPE_MASK)
174 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
176 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
178 static SIZE menucharsize;
179 static UINT ODitemheight; /* default owner drawn item height */
181 /* Use global popup window because there's no way 2 menus can
182 * be tracked at the same time. */
183 static HWND top_popup;
184 static HMENU top_popup_hmenu;
186 /* Flag set by EndMenu() to force an exit from menu tracking */
187 static BOOL fEndMenu = FALSE;
189 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
191 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
193 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
195 /*********************************************************************
196 * menu class descriptor
198 const struct builtin_class_descr MENU_builtin_class =
200 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
201 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
202 NULL, /* procA (winproc is Unicode only) */
203 PopupMenuWndProc, /* procW */
204 sizeof(HMENU), /* extra */
205 IDC_ARROW, /* cursor */
206 (HBRUSH)(COLOR_MENU+1) /* brush */
210 /***********************************************************************
211 * debug_print_menuitem
213 * Print a menuitem in readable form.
216 #define debug_print_menuitem(pre, mp, post) \
217 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
219 #define MENUOUT(text) \
220 TRACE("%s%s", (count++ ? "," : ""), (text))
222 #define MENUFLAG(bit,text) \
223 do { \
224 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
225 } while (0)
227 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
228 const char *postfix)
230 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
231 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
232 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
233 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
234 TRACE("%s ", prefix);
235 if (mp) {
236 UINT flags = mp->fType;
237 TRACE( "{ ID=0x%lx", mp->wID);
238 if ( mp->hSubMenu)
239 TRACE( ", Sub=%p", mp->hSubMenu);
240 if (flags) {
241 int count = 0;
242 TRACE( ", fType=");
243 MENUFLAG( MFT_SEPARATOR, "sep");
244 MENUFLAG( MFT_OWNERDRAW, "own");
245 MENUFLAG( MFT_BITMAP, "bit");
246 MENUFLAG(MF_POPUP, "pop");
247 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
248 MENUFLAG(MFT_MENUBREAK, "brk");
249 MENUFLAG(MFT_RADIOCHECK, "radio");
250 MENUFLAG(MFT_RIGHTORDER, "rorder");
251 MENUFLAG(MF_SYSMENU, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
253 if (flags)
254 TRACE( "+0x%x", flags);
256 flags = mp->fState;
257 if (flags) {
258 int count = 0;
259 TRACE( ", State=");
260 MENUFLAG(MFS_GRAYED, "grey");
261 MENUFLAG(MFS_DEFAULT, "default");
262 MENUFLAG(MFS_DISABLED, "dis");
263 MENUFLAG(MFS_CHECKED, "check");
264 MENUFLAG(MFS_HILITE, "hi");
265 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
266 MENUFLAG(MF_MOUSESELECT, "mouse");
267 if (flags)
268 TRACE( "+0x%x", flags);
270 if (mp->hCheckBit)
271 TRACE( ", Chk=%p", mp->hCheckBit);
272 if (mp->hUnCheckBit)
273 TRACE( ", Unc=%p", mp->hUnCheckBit);
274 if (mp->text)
275 TRACE( ", Text=%s", debugstr_w(mp->text));
276 if (mp->dwItemData)
277 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
278 if (mp->hbmpItem)
280 if( IS_MAGIC_BITMAP(mp->hbmpItem))
281 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
282 else
283 TRACE( ", hbitmap=%p", mp->hbmpItem);
285 TRACE( " }");
286 } else
287 TRACE( "NULL");
288 TRACE(" %s\n", postfix);
291 #undef MENUOUT
292 #undef MENUFLAG
295 /***********************************************************************
296 * MENU_GetMenu
298 * Validate the given menu handle and returns the menu structure pointer.
300 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
302 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
303 if (!menu || menu->wMagic != MENU_MAGIC)
305 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
306 menu = NULL;
308 return menu;
311 /***********************************************************************
312 * get_win_sys_menu
314 * Get the system menu of a window
316 static HMENU get_win_sys_menu( HWND hwnd )
318 HMENU ret = 0;
319 WND *win = WIN_GetPtr( hwnd );
320 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
322 ret = win->hSysMenu;
323 WIN_ReleasePtr( win );
325 return ret;
328 /***********************************************************************
329 * get_menu_font
331 static HFONT get_menu_font( BOOL bold )
333 static HFONT hMenuFont, hMenuFontBold;
335 HFONT ret = bold ? hMenuFontBold : hMenuFont;
337 if (!ret)
339 NONCLIENTMETRICSW ncm;
340 HFONT prev;
342 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
343 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
345 if (bold)
347 ncm.lfMenuFont.lfWeight += 300;
348 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
350 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
351 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
352 ret, NULL );
353 if (prev)
355 /* another thread beat us to it */
356 DeleteObject( ret );
357 ret = prev;
360 return ret;
363 /***********************************************************************
364 * get_arrow_bitmap
366 static HBITMAP get_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap;
370 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
371 return arrow_bitmap;
374 /***********************************************************************
375 * get_down_arrow_bitmap
377 static HBITMAP get_down_arrow_bitmap(void)
379 static HBITMAP arrow_bitmap;
381 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
382 return arrow_bitmap;
385 /***********************************************************************
386 * get_down_arrow_inactive_bitmap
388 static HBITMAP get_down_arrow_inactive_bitmap(void)
390 static HBITMAP arrow_bitmap;
392 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
393 return arrow_bitmap;
396 /***********************************************************************
397 * get_up_arrow_bitmap
399 static HBITMAP get_up_arrow_bitmap(void)
401 static HBITMAP arrow_bitmap;
403 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
404 return arrow_bitmap;
407 /***********************************************************************
408 * get_up_arrow_inactive_bitmap
410 static HBITMAP get_up_arrow_inactive_bitmap(void)
412 static HBITMAP arrow_bitmap;
414 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
415 return arrow_bitmap;
418 /***********************************************************************
419 * MENU_CopySysPopup
421 * Return the default system menu.
423 static HMENU MENU_CopySysPopup(void)
425 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
426 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
428 if( hMenu ) {
429 MENUINFO minfo;
430 MENUITEMINFOW miteminfo;
431 POPUPMENU* menu = MENU_GetMenu(hMenu);
432 menu->wFlags |= MF_SYSMENU | MF_POPUP;
433 /* decorate the menu with bitmaps */
434 minfo.cbSize = sizeof( MENUINFO);
435 minfo.dwStyle = MNS_CHECKORBMP;
436 minfo.fMask = MIM_STYLE;
437 SetMenuInfo( hMenu, &minfo);
438 miteminfo.cbSize = sizeof( MENUITEMINFOW);
439 miteminfo.fMask = MIIM_BITMAP;
440 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
441 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
443 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
446 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
447 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
448 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
450 else
451 ERR("Unable to load default system menu\n" );
453 TRACE("returning %p.\n", hMenu );
455 return hMenu;
459 /**********************************************************************
460 * MENU_GetSysMenu
462 * Create a copy of the system menu. System menu in Windows is
463 * a special menu bar with the single entry - system menu popup.
464 * This popup is presented to the outside world as a "system menu".
465 * However, the real system menu handle is sometimes seen in the
466 * WM_MENUSELECT parameters (and Word 6 likes it this way).
468 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
470 HMENU hMenu;
472 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
473 if ((hMenu = CreateMenu()))
475 POPUPMENU *menu = MENU_GetMenu(hMenu);
476 menu->wFlags = MF_SYSMENU;
477 menu->hWnd = WIN_GetFullHandle( hWnd );
478 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
480 if (!hPopupMenu)
481 hPopupMenu = MENU_CopySysPopup();
483 if (hPopupMenu)
485 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
486 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
488 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
489 (UINT_PTR)hPopupMenu, NULL );
491 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
492 menu->items[0].fState = 0;
493 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
495 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
496 return hMenu;
498 DestroyMenu( hMenu );
500 ERR("failed to load system menu!\n");
501 return 0;
505 /***********************************************************************
506 * MENU_InitSysMenuPopup
508 * Grey the appropriate items in System menu.
510 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
512 BOOL gray;
514 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
515 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = ((style & WS_MAXIMIZE) != 0);
517 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
518 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
519 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
520 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
521 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
522 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
523 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
524 gray = (clsStyle & CS_NOCLOSE) != 0;
526 /* The menu item must keep its state if it's disabled */
527 if(gray)
528 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
532 /******************************************************************************
534 * UINT MENU_GetStartOfNextColumn(
535 * HMENU hMenu )
537 *****************************************************************************/
539 static UINT MENU_GetStartOfNextColumn(
540 HMENU hMenu )
542 POPUPMENU *menu = MENU_GetMenu(hMenu);
543 UINT i;
545 if(!menu)
546 return NO_SELECTED_ITEM;
548 i = menu->FocusedItem + 1;
549 if( i == NO_SELECTED_ITEM )
550 return i;
552 for( ; i < menu->nItems; ++i ) {
553 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
554 return i;
557 return NO_SELECTED_ITEM;
561 /******************************************************************************
563 * UINT MENU_GetStartOfPrevColumn(
564 * HMENU hMenu )
566 *****************************************************************************/
568 static UINT MENU_GetStartOfPrevColumn(
569 HMENU hMenu )
571 POPUPMENU *menu = MENU_GetMenu(hMenu);
572 UINT i;
574 if( !menu )
575 return NO_SELECTED_ITEM;
577 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
578 return NO_SELECTED_ITEM;
580 /* Find the start of the column */
582 for(i = menu->FocusedItem; i != 0 &&
583 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
584 --i); /* empty */
586 if(i == 0)
587 return NO_SELECTED_ITEM;
589 for(--i; i != 0; --i) {
590 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
591 break;
594 TRACE("ret %d.\n", i );
596 return i;
601 /***********************************************************************
602 * MENU_FindItem
604 * Find a menu item. Return a pointer on the item, and modifies *hmenu
605 * in case the item was in a sub-menu.
607 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
609 POPUPMENU *menu;
610 MENUITEM *fallback = NULL;
611 UINT fallback_pos = 0;
612 UINT i;
614 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
615 if (wFlags & MF_BYPOSITION)
617 if (*nPos >= menu->nItems) return NULL;
618 return &menu->items[*nPos];
620 else
622 MENUITEM *item = menu->items;
623 for (i = 0; i < menu->nItems; i++, item++)
625 if (item->fType & MF_POPUP)
627 HMENU hsubmenu = item->hSubMenu;
628 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
629 if (subitem)
631 *hmenu = hsubmenu;
632 return subitem;
634 else if (item->wID == *nPos)
636 /* fallback to this item if nothing else found */
637 fallback_pos = i;
638 fallback = item;
641 else if (item->wID == *nPos)
643 *nPos = i;
644 return item;
649 if (fallback)
650 *nPos = fallback_pos;
652 return fallback;
655 /***********************************************************************
656 * MENU_FindSubMenu
658 * Find a Sub menu. Return the position of the submenu, and modifies
659 * *hmenu in case it is found in another sub-menu.
660 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
662 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
664 POPUPMENU *menu;
665 UINT i;
666 MENUITEM *item;
667 if (((*hmenu)==(HMENU)0xffff) ||
668 (!(menu = MENU_GetMenu(*hmenu))))
669 return NO_SELECTED_ITEM;
670 item = menu->items;
671 for (i = 0; i < menu->nItems; i++, item++) {
672 if(!(item->fType & MF_POPUP)) continue;
673 if (item->hSubMenu == hSubTarget) {
674 return i;
676 else {
677 HMENU hsubmenu = item->hSubMenu;
678 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
679 if (pos != NO_SELECTED_ITEM) {
680 *hmenu = hsubmenu;
681 return pos;
685 return NO_SELECTED_ITEM;
688 /***********************************************************************
689 * MENU_FreeItemData
691 static void MENU_FreeItemData( MENUITEM* item )
693 /* delete text */
694 HeapFree( GetProcessHeap(), 0, item->text );
697 /***********************************************************************
698 * MENU_AdjustMenuItemRect
700 * Adjust menu item rectangle according to scrolling state.
702 static void
703 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
705 if (menu->bScrolling)
707 UINT arrow_bitmap_height;
708 BITMAP bmp;
710 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
711 arrow_bitmap_height = bmp.bmHeight;
712 rect->top += arrow_bitmap_height - menu->nScrollPos;
713 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
718 /***********************************************************************
719 * MENU_FindItemByCoords
721 * Find the item at the specified coordinates (screen coords). Does
722 * not work for child windows and therefore should not be called for
723 * an arbitrary system menu.
725 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
726 POINT pt, UINT *pos )
728 MENUITEM *item;
729 UINT i;
730 RECT rect;
732 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
733 pt.x -= rect.left;
734 pt.y -= rect.top;
735 item = menu->items;
736 for (i = 0; i < menu->nItems; i++, item++)
738 rect = item->rect;
739 MENU_AdjustMenuItemRect(menu, &rect);
740 if (PtInRect(&rect, pt))
742 if (pos) *pos = i;
743 return item;
746 return NULL;
750 /***********************************************************************
751 * MENU_FindItemByKey
753 * Find the menu item selected by a key press.
754 * Return item id, -1 if none, -2 if we should close the menu.
756 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
757 WCHAR key, BOOL forceMenuChar )
759 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
761 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
763 if (hmenu)
765 POPUPMENU *menu = MENU_GetMenu( hmenu );
766 MENUITEM *item = menu->items;
767 LRESULT menuchar;
769 if( !forceMenuChar )
771 UINT i;
773 for (i = 0; i < menu->nItems; i++, item++)
775 if( item->text)
777 WCHAR *p = item->text - 2;
780 p = strchrW (p + 2, '&');
782 while (p != NULL && p [1] == '&');
783 if (p && (toupperW(p[1]) == toupperW(key))) return i;
787 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
788 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
789 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
790 if (HIWORD(menuchar) == 1) return (UINT)(-2);
792 return (UINT)(-1);
796 /***********************************************************************
797 * MENU_GetBitmapItemSize
799 * Get the size of a bitmap item.
801 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
802 HWND hwndOwner)
804 BITMAP bm;
805 HBITMAP bmp = lpitem->hbmpItem;
807 size->cx = size->cy = 0;
809 /* check if there is a magic menu item associated with this item */
810 switch( (INT_PTR) bmp )
812 case (INT_PTR)HBMMENU_CALLBACK:
814 MEASUREITEMSTRUCT measItem;
815 measItem.CtlType = ODT_MENU;
816 measItem.CtlID = 0;
817 measItem.itemID = lpitem->wID;
818 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
819 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
820 measItem.itemData = lpitem->dwItemData;
821 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
822 size->cx = measItem.itemWidth;
823 size->cy = measItem.itemHeight;
824 return;
826 break;
827 case (INT_PTR)HBMMENU_SYSTEM:
828 if (lpitem->dwItemData)
830 bmp = (HBITMAP)lpitem->dwItemData;
831 break;
833 /* fall through */
834 case (INT_PTR)HBMMENU_MBAR_RESTORE:
835 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
836 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
837 case (INT_PTR)HBMMENU_MBAR_CLOSE:
838 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
839 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
840 size->cy = size->cx;
841 return;
842 case (INT_PTR)HBMMENU_POPUP_CLOSE:
843 case (INT_PTR)HBMMENU_POPUP_RESTORE:
844 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
845 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
846 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
847 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
848 return;
850 if (GetObjectW(bmp, sizeof(bm), &bm ))
852 size->cx = bm.bmWidth;
853 size->cy = bm.bmHeight;
857 /***********************************************************************
858 * MENU_DrawBitmapItem
860 * Draw a bitmap item.
862 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
863 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
865 BITMAP bm;
866 DWORD rop;
867 HDC hdcMem;
868 HBITMAP bmp;
869 int w = rect->right - rect->left;
870 int h = rect->bottom - rect->top;
871 int bmp_xoffset = 0;
872 int left, top;
873 HBITMAP hbmToDraw = lpitem->hbmpItem;
874 bmp = hbmToDraw;
876 /* Check if there is a magic menu item associated with this item */
877 if (IS_MAGIC_BITMAP(hbmToDraw))
879 UINT flags = 0;
880 WCHAR bmchr = 0;
881 RECT r;
883 switch((INT_PTR)hbmToDraw)
885 case (INT_PTR)HBMMENU_SYSTEM:
886 if (lpitem->dwItemData)
888 bmp = (HBITMAP)lpitem->dwItemData;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
891 else
893 static HBITMAP hBmpSysMenu;
895 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
896 bmp = hBmpSysMenu;
897 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
898 /* only use right half of the bitmap */
899 bmp_xoffset = bm.bmWidth / 2;
900 bm.bmWidth -= bmp_xoffset;
902 goto got_bitmap;
903 case (INT_PTR)HBMMENU_MBAR_RESTORE:
904 flags = DFCS_CAPTIONRESTORE;
905 break;
906 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
907 flags = DFCS_CAPTIONMIN;
908 break;
909 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
910 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
911 break;
912 case (INT_PTR)HBMMENU_MBAR_CLOSE:
913 flags = DFCS_CAPTIONCLOSE;
914 break;
915 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
916 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
917 break;
918 case (INT_PTR)HBMMENU_CALLBACK:
920 DRAWITEMSTRUCT drawItem;
921 drawItem.CtlType = ODT_MENU;
922 drawItem.CtlID = 0;
923 drawItem.itemID = lpitem->wID;
924 drawItem.itemAction = odaction;
925 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
926 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
927 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
928 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
929 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
930 drawItem.hwndItem = (HWND)hmenu;
931 drawItem.hDC = hdc;
932 drawItem.itemData = lpitem->dwItemData;
933 drawItem.rcItem = *rect;
934 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
935 return;
937 break;
938 case (INT_PTR)HBMMENU_POPUP_CLOSE:
939 bmchr = 0x72;
940 break;
941 case (INT_PTR)HBMMENU_POPUP_RESTORE:
942 bmchr = 0x32;
943 break;
944 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
945 bmchr = 0x31;
946 break;
947 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
948 bmchr = 0x30;
949 break;
950 default:
951 FIXME("Magic %p not implemented\n", hbmToDraw);
952 return;
954 if (bmchr)
956 /* draw the magic bitmaps using marlett font characters */
957 /* FIXME: fontsize and the position (x,y) could probably be better */
958 HFONT hfont, hfontsav;
959 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
960 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
961 { 'M','a','r','l','e','t','t',0 } };
962 logfont.lfHeight = min( h, w) - 5 ;
963 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
964 hfont = CreateFontIndirectW( &logfont);
965 hfontsav = SelectObject(hdc, hfont);
966 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
967 SelectObject(hdc, hfontsav);
968 DeleteObject( hfont);
970 else
972 r = *rect;
973 InflateRect( &r, -1, -1 );
974 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
975 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
977 return;
980 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
982 got_bitmap:
983 hdcMem = CreateCompatibleDC( hdc );
984 SelectObject( hdcMem, bmp );
986 /* handle fontsize > bitmap_height */
987 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
988 left=rect->left;
989 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
990 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
991 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
992 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
993 DeleteDC( hdcMem );
997 /***********************************************************************
998 * MENU_CalcItemSize
1000 * Calculate the size of the menu item and store it in lpitem->rect.
1002 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1003 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1005 WCHAR *p;
1006 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1007 UINT arrow_bitmap_width;
1008 BITMAP bm;
1009 INT itemheight;
1011 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1012 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1013 (menuBar ? " (MenuBar)" : ""));
1015 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1016 arrow_bitmap_width = bm.bmWidth;
1018 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1019 if( !menucharsize.cx ) {
1020 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1021 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1022 * but it is unlikely an application will depend on that */
1023 ODitemheight = HIWORD( GetDialogBaseUnits());
1026 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1028 if (lpitem->fType & MF_OWNERDRAW)
1030 MEASUREITEMSTRUCT mis;
1031 mis.CtlType = ODT_MENU;
1032 mis.CtlID = 0;
1033 mis.itemID = lpitem->wID;
1034 mis.itemData = lpitem->dwItemData;
1035 mis.itemHeight = ODitemheight;
1036 mis.itemWidth = 0;
1037 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1038 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1039 * width of a menufont character to the width of an owner-drawn menu.
1041 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1042 if (menuBar) {
1043 /* under at least win95 you seem to be given a standard
1044 height for the menu and the height value is ignored */
1045 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1046 } else
1047 lpitem->rect.bottom += mis.itemHeight;
1049 TRACE("id=%04lx size=%dx%d\n",
1050 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1051 lpitem->rect.bottom-lpitem->rect.top);
1052 return;
1055 if (lpitem->fType & MF_SEPARATOR)
1057 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1058 if( !menuBar)
1059 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1060 return;
1063 itemheight = 0;
1064 lpitem->xTab = 0;
1066 if (!menuBar) {
1067 if (lpitem->hbmpItem) {
1068 SIZE size;
1070 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1071 /* Keep the size of the bitmap in callback mode to be able
1072 * to draw it correctly */
1073 lpitem->bmpsize = size;
1074 lppop->textOffset = max( lppop->textOffset, size.cx);
1075 lpitem->rect.right += size.cx + 2;
1076 itemheight = size.cy + 2;
1078 if( !(lppop->dwStyle & MNS_NOCHECK))
1079 lpitem->rect.right += check_bitmap_width;
1080 lpitem->rect.right += 4 + menucharsize.cx;
1081 lpitem->xTab = lpitem->rect.right;
1082 lpitem->rect.right += arrow_bitmap_width;
1083 } else if (lpitem->hbmpItem) { /* menuBar */
1084 SIZE size;
1086 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1087 lpitem->bmpsize = size;
1088 lpitem->rect.right += size.cx;
1089 if( lpitem->text) lpitem->rect.right += 2;
1090 itemheight = size.cy;
1093 /* it must be a text item - unless it's the system menu */
1094 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1095 HFONT hfontOld = NULL;
1096 RECT rc = lpitem->rect;
1097 LONG txtheight, txtwidth;
1099 if ( lpitem->fState & MFS_DEFAULT ) {
1100 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1102 if (menuBar) {
1103 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1104 DT_SINGLELINE|DT_CALCRECT);
1105 lpitem->rect.right += rc.right - rc.left;
1106 itemheight = max( max( itemheight, txtheight),
1107 GetSystemMetrics( SM_CYMENU) - 1);
1108 lpitem->rect.right += 2 * menucharsize.cx;
1109 } else {
1110 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1111 RECT tmprc = rc;
1112 LONG tmpheight;
1113 int n = (int)( p - lpitem->text);
1114 /* Item contains a tab (only meaningful in popup menus) */
1115 /* get text size before the tab */
1116 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1117 DT_SINGLELINE|DT_CALCRECT);
1118 txtwidth = rc.right - rc.left;
1119 p += 1; /* advance past the Tab */
1120 /* get text size after the tab */
1121 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1122 DT_SINGLELINE|DT_CALCRECT);
1123 lpitem->xTab += txtwidth;
1124 txtheight = max( txtheight, tmpheight);
1125 txtwidth += menucharsize.cx + /* space for the tab */
1126 tmprc.right - tmprc.left; /* space for the short cut */
1127 } else {
1128 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1129 DT_SINGLELINE|DT_CALCRECT);
1130 txtwidth = rc.right - rc.left;
1131 lpitem->xTab += txtwidth;
1133 lpitem->rect.right += 2 + txtwidth;
1134 itemheight = max( itemheight,
1135 max( txtheight + 2, menucharsize.cy + 4));
1137 if (hfontOld) SelectObject (hdc, hfontOld);
1138 } else if( menuBar) {
1139 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1141 lpitem->rect.bottom += itemheight;
1142 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1146 /***********************************************************************
1147 * MENU_GetMaxPopupHeight
1149 static UINT
1150 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1152 if (lppop->cyMax)
1153 return lppop->cyMax;
1154 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1158 /***********************************************************************
1159 * MENU_PopupMenuCalcSize
1161 * Calculate the size of a popup menu.
1163 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1165 MENUITEM *lpitem;
1166 HDC hdc;
1167 UINT start, i;
1168 int textandbmp = FALSE;
1169 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1171 lppop->Width = lppop->Height = 0;
1172 if (lppop->nItems == 0) return;
1173 hdc = GetDC( 0 );
1175 SelectObject( hdc, get_menu_font(FALSE));
1177 start = 0;
1178 maxX = 2 + 1;
1180 lppop->textOffset = 0;
1182 while (start < lppop->nItems)
1184 lpitem = &lppop->items[start];
1185 orgX = maxX;
1186 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1187 orgX += MENU_COL_SPACE;
1188 orgY = MENU_TOP_MARGIN;
1190 maxTab = maxTabWidth = 0;
1191 /* Parse items until column break or end of menu */
1192 for (i = start; i < lppop->nItems; i++, lpitem++)
1194 if ((i != start) &&
1195 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1197 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1198 maxX = max( maxX, lpitem->rect.right );
1199 orgY = lpitem->rect.bottom;
1200 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1202 maxTab = max( maxTab, lpitem->xTab );
1203 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1205 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1208 /* Finish the column (set all items to the largest width found) */
1209 maxX = max( maxX, maxTab + maxTabWidth );
1210 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1212 lpitem->rect.right = maxX;
1213 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1214 lpitem->xTab = maxTab;
1217 lppop->Height = max( lppop->Height, orgY );
1220 lppop->Width = maxX;
1221 /* if none of the items have both text and bitmap then
1222 * the text and bitmaps are all aligned on the left. If there is at
1223 * least one item with both text and bitmap then bitmaps are
1224 * on the left and texts left aligned with the right hand side
1225 * of the bitmaps */
1226 if( !textandbmp) lppop->textOffset = 0;
1228 /* space for 3d border */
1229 lppop->Height += MENU_BOTTOM_MARGIN;
1230 lppop->Width += 2;
1232 /* Adjust popup height if it exceeds maximum */
1233 maxHeight = MENU_GetMaxPopupHeight(lppop);
1234 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1235 if (lppop->Height >= maxHeight)
1237 lppop->Height = maxHeight;
1238 lppop->bScrolling = TRUE;
1240 else
1242 lppop->bScrolling = FALSE;
1245 ReleaseDC( 0, hdc );
1249 /***********************************************************************
1250 * MENU_MenuBarCalcSize
1252 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1253 * height is off by 1 pixel which causes lengthy window relocations when
1254 * active document window is maximized/restored.
1256 * Calculate the size of the menu bar.
1258 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1259 LPPOPUPMENU lppop, HWND hwndOwner )
1261 MENUITEM *lpitem;
1262 UINT start, i, helpPos;
1263 int orgX, orgY, maxY;
1265 if ((lprect == NULL) || (lppop == NULL)) return;
1266 if (lppop->nItems == 0) return;
1267 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1268 lppop->Width = lprect->right - lprect->left;
1269 lppop->Height = 0;
1270 maxY = lprect->top+1;
1271 start = 0;
1272 helpPos = ~0U;
1273 lppop->textOffset = 0;
1274 while (start < lppop->nItems)
1276 lpitem = &lppop->items[start];
1277 orgX = lprect->left;
1278 orgY = maxY;
1280 /* Parse items until line break or end of menu */
1281 for (i = start; i < lppop->nItems; i++, lpitem++)
1283 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1284 if ((i != start) &&
1285 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1287 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1288 debug_print_menuitem (" item: ", lpitem, "");
1289 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1291 if (lpitem->rect.right > lprect->right)
1293 if (i != start) break;
1294 else lpitem->rect.right = lprect->right;
1296 maxY = max( maxY, lpitem->rect.bottom );
1297 orgX = lpitem->rect.right;
1300 /* Finish the line (set all items to the largest height found) */
1301 while (start < i) lppop->items[start++].rect.bottom = maxY;
1304 lprect->bottom = maxY;
1305 lppop->Height = lprect->bottom - lprect->top;
1307 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1308 /* the last item (if several lines, only move the last line) */
1309 if (helpPos == ~0U) return;
1310 lpitem = &lppop->items[lppop->nItems-1];
1311 orgY = lpitem->rect.top;
1312 orgX = lprect->right;
1313 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1314 if (lpitem->rect.top != orgY) break; /* Other line */
1315 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1316 lpitem->rect.left += orgX - lpitem->rect.right;
1317 lpitem->rect.right = orgX;
1318 orgX = lpitem->rect.left;
1323 /***********************************************************************
1324 * MENU_DrawScrollArrows
1326 * Draw scroll arrows.
1328 static void
1329 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1331 HDC hdcMem = CreateCompatibleDC(hdc);
1332 HBITMAP hOrigBitmap;
1333 UINT arrow_bitmap_width, arrow_bitmap_height;
1334 BITMAP bmp;
1335 RECT rect;
1337 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1338 arrow_bitmap_width = bmp.bmWidth;
1339 arrow_bitmap_height = bmp.bmHeight;
1342 if (lppop->nScrollPos)
1343 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1344 else
1345 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1346 rect.left = 0;
1347 rect.top = 0;
1348 rect.right = lppop->Width;
1349 rect.bottom = arrow_bitmap_height;
1350 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1351 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1352 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1353 rect.top = lppop->Height - arrow_bitmap_height;
1354 rect.bottom = lppop->Height;
1355 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1356 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1357 SelectObject(hdcMem, get_down_arrow_bitmap());
1358 else
1359 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1360 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1361 lppop->Height - arrow_bitmap_height,
1362 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1363 SelectObject(hdcMem, hOrigBitmap);
1364 DeleteDC(hdcMem);
1368 /***********************************************************************
1369 * draw_popup_arrow
1371 * Draws the popup-menu arrow.
1373 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1374 UINT arrow_bitmap_height)
1376 HDC hdcMem = CreateCompatibleDC( hdc );
1377 HBITMAP hOrigBitmap;
1379 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1380 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1381 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1382 arrow_bitmap_width, arrow_bitmap_height,
1383 hdcMem, 0, 0, SRCCOPY );
1384 SelectObject( hdcMem, hOrigBitmap );
1385 DeleteDC( hdcMem );
1387 /***********************************************************************
1388 * MENU_DrawMenuItem
1390 * Draw a single menu item.
1392 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1393 UINT height, BOOL menuBar, UINT odaction )
1395 RECT rect;
1396 BOOL flat_menu = FALSE;
1397 int bkgnd;
1398 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1399 POPUPMENU *menu = MENU_GetMenu(hmenu);
1400 RECT bmprc;
1402 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1404 if (!menuBar) {
1405 BITMAP bmp;
1406 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1407 arrow_bitmap_width = bmp.bmWidth;
1408 arrow_bitmap_height = bmp.bmHeight;
1411 if (lpitem->fType & MF_SYSMENU)
1413 if( !IsIconic(hwnd) )
1414 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1415 return;
1418 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1419 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1421 /* Setup colors */
1423 if (lpitem->fState & MF_HILITE)
1425 if(menuBar && !flat_menu) {
1426 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1427 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1428 } else {
1429 if(lpitem->fState & MF_GRAYED)
1430 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1431 else
1432 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1433 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1436 else
1438 if (lpitem->fState & MF_GRAYED)
1439 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1440 else
1441 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1442 SetBkColor( hdc, GetSysColor( bkgnd ) );
1445 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1446 rect = lpitem->rect;
1447 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1449 if (lpitem->fType & MF_OWNERDRAW)
1452 ** Experimentation under Windows reveals that an owner-drawn
1453 ** menu is given the rectangle which includes the space it requested
1454 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1455 ** and a popup-menu arrow. This is the value of lpitem->rect.
1456 ** Windows will leave all drawing to the application except for
1457 ** the popup-menu arrow. Windows always draws that itself, after
1458 ** the menu owner has finished drawing.
1460 DRAWITEMSTRUCT dis;
1462 dis.CtlType = ODT_MENU;
1463 dis.CtlID = 0;
1464 dis.itemID = lpitem->wID;
1465 dis.itemData = lpitem->dwItemData;
1466 dis.itemState = 0;
1467 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1468 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1469 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1470 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1471 dis.hwndItem = (HWND)hmenu;
1472 dis.hDC = hdc;
1473 dis.rcItem = rect;
1474 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1475 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1476 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1477 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1478 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1479 /* Draw the popup-menu arrow */
1480 if (lpitem->fType & MF_POPUP)
1481 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1482 arrow_bitmap_height);
1483 return;
1486 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1488 if (lpitem->fState & MF_HILITE)
1490 if (flat_menu)
1492 InflateRect (&rect, -1, -1);
1493 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1494 InflateRect (&rect, 1, 1);
1495 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1497 else
1499 if(menuBar)
1500 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1501 else
1502 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1505 else
1506 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1508 SetBkMode( hdc, TRANSPARENT );
1510 /* vertical separator */
1511 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1513 HPEN oldPen;
1514 RECT rc = rect;
1516 rc.left -= MENU_COL_SPACE / 2 + 1;
1517 rc.top = 3;
1518 rc.bottom = height - 3;
1519 if (flat_menu)
1521 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1522 MoveToEx( hdc, rc.left, rc.top, NULL );
1523 LineTo( hdc, rc.left, rc.bottom );
1524 SelectObject( hdc, oldPen );
1526 else
1527 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1530 /* horizontal separator */
1531 if (lpitem->fType & MF_SEPARATOR)
1533 HPEN oldPen;
1534 RECT rc = rect;
1536 rc.left++;
1537 rc.right--;
1538 rc.top = ( rc.top + rc.bottom) / 2;
1539 if (flat_menu)
1541 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1542 MoveToEx( hdc, rc.left, rc.top, NULL );
1543 LineTo( hdc, rc.right, rc.top );
1544 SelectObject( hdc, oldPen );
1546 else
1547 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1548 return;
1551 /* helper lines for debugging */
1552 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1553 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1554 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1555 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1558 if (lpitem->hbmpItem) {
1559 /* calculate the bitmap rectangle in coordinates relative
1560 * to the item rectangle */
1561 if( menuBar) {
1562 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1563 bmprc.left = 3;
1564 else
1565 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1567 else if (menu->dwStyle & MNS_NOCHECK)
1568 bmprc.left = 4;
1569 else if (menu->dwStyle & MNS_CHECKORBMP)
1570 bmprc.left = 2;
1571 else
1572 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1573 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1574 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1575 bmprc.top = 0;
1576 else
1577 bmprc.top = (rect.bottom - rect.top -
1578 lpitem->bmpsize.cy) / 2;
1579 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1582 if (!menuBar)
1584 HBITMAP bm;
1585 INT y = rect.top + rect.bottom;
1586 RECT rc = rect;
1587 int checked = FALSE;
1588 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1589 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1590 /* Draw the check mark
1592 * FIXME:
1593 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1595 if( !(menu->dwStyle & MNS_NOCHECK)) {
1596 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1597 lpitem->hUnCheckBit;
1598 if (bm) /* we have a custom bitmap */
1600 HDC hdcMem = CreateCompatibleDC( hdc );
1602 SelectObject( hdcMem, bm );
1603 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1604 check_bitmap_width, check_bitmap_height,
1605 hdcMem, 0, 0, SRCCOPY );
1606 DeleteDC( hdcMem );
1607 checked = TRUE;
1609 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1611 RECT r;
1612 HBITMAP bm = CreateBitmap( check_bitmap_width,
1613 check_bitmap_height, 1, 1, NULL );
1614 HDC hdcMem = CreateCompatibleDC( hdc );
1616 SelectObject( hdcMem, bm );
1617 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1618 DrawFrameControl( hdcMem, &r, DFC_MENU,
1619 (lpitem->fType & MFT_RADIOCHECK) ?
1620 DFCS_MENUBULLET : DFCS_MENUCHECK );
1621 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1622 hdcMem, 0, 0, SRCCOPY );
1623 DeleteDC( hdcMem );
1624 DeleteObject( bm );
1625 checked = TRUE;
1628 if( lpitem->hbmpItem &&
1629 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1630 POINT origorg;
1631 /* some applications make this assumption on the DC's origin */
1632 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1633 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1634 odaction, FALSE);
1635 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1637 /* Draw the popup-menu arrow */
1638 if (lpitem->fType & MF_POPUP)
1639 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1640 arrow_bitmap_height);
1641 rect.left += 4;
1642 if( !(menu->dwStyle & MNS_NOCHECK))
1643 rect.left += check_bitmap_width;
1644 rect.right -= arrow_bitmap_width;
1646 else if( lpitem->hbmpItem)
1647 { /* Draw the bitmap */
1648 POINT origorg;
1650 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1651 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1652 odaction, menuBar);
1653 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1655 /* process text if present */
1656 if (lpitem->text)
1658 register int i;
1659 HFONT hfontOld = 0;
1661 UINT uFormat = (menuBar) ?
1662 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1663 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1665 if( !(menu->dwStyle & MNS_CHECKORBMP))
1666 rect.left += menu->textOffset;
1668 if ( lpitem->fState & MFS_DEFAULT )
1670 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1673 if (menuBar) {
1674 if( lpitem->hbmpItem)
1675 rect.left += lpitem->bmpsize.cx;
1676 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1677 rect.left += menucharsize.cx;
1678 rect.right -= menucharsize.cx;
1681 for (i = 0; lpitem->text[i]; i++)
1682 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1683 break;
1685 if(lpitem->fState & MF_GRAYED)
1687 if (!(lpitem->fState & MF_HILITE) )
1689 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1690 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1691 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1692 --rect.left; --rect.top; --rect.right; --rect.bottom;
1694 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1697 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1699 /* paint the shortcut text */
1700 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1702 if (lpitem->text[i] == '\t')
1704 rect.left = lpitem->xTab;
1705 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1707 else
1709 rect.right = lpitem->xTab;
1710 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1713 if(lpitem->fState & MF_GRAYED)
1715 if (!(lpitem->fState & MF_HILITE) )
1717 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1718 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1719 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1720 --rect.left; --rect.top; --rect.right; --rect.bottom;
1722 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1724 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1727 if (hfontOld)
1728 SelectObject (hdc, hfontOld);
1733 /***********************************************************************
1734 * MENU_DrawPopupMenu
1736 * Paint a popup menu.
1738 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1740 HBRUSH hPrevBrush = 0;
1741 RECT rect;
1743 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1745 GetClientRect( hwnd, &rect );
1747 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1748 && (SelectObject( hdc, get_menu_font(FALSE))))
1750 HPEN hPrevPen;
1752 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1754 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1755 if( hPrevPen )
1757 POPUPMENU *menu;
1758 BOOL flat_menu = FALSE;
1760 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1761 if (flat_menu)
1762 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1763 else
1764 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1766 if( (menu = MENU_GetMenu( hmenu )))
1768 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1769 /* draw menu items */
1770 if( menu->nItems)
1772 MENUITEM *item;
1773 UINT u;
1775 item = menu->items;
1776 for( u = menu->nItems; u > 0; u--, item++)
1777 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1778 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1780 /* draw scroll arrows */
1781 if (menu->bScrolling)
1782 MENU_DrawScrollArrows(menu, hdc);
1784 } else
1786 SelectObject( hdc, hPrevBrush );
1791 /***********************************************************************
1792 * MENU_DrawMenuBar
1794 * Paint a menu bar. Returns the height of the menu bar.
1795 * called from [windows/nonclient.c]
1797 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1798 BOOL suppress_draw)
1800 LPPOPUPMENU lppop;
1801 HFONT hfontOld = 0;
1802 HMENU hMenu = GetMenu(hwnd);
1804 lppop = MENU_GetMenu( hMenu );
1805 if (lppop == NULL || lprect == NULL)
1807 return GetSystemMetrics(SM_CYMENU);
1810 if (suppress_draw)
1812 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1814 if (lppop->Height == 0)
1815 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1817 lprect->bottom = lprect->top + lppop->Height;
1819 if (hfontOld) SelectObject( hDC, hfontOld);
1820 return lppop->Height;
1822 else
1823 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1827 /***********************************************************************
1828 * MENU_ShowPopup
1830 * Display a popup menu.
1832 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1833 INT x, INT y, INT xanchor, INT yanchor )
1835 POPUPMENU *menu;
1836 INT width, height;
1837 POINT pt;
1838 HMONITOR monitor;
1839 MONITORINFO info;
1841 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1842 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1844 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1845 if (menu->FocusedItem != NO_SELECTED_ITEM)
1847 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1848 menu->FocusedItem = NO_SELECTED_ITEM;
1851 /* store the owner for DrawItem */
1852 menu->hwndOwner = hwndOwner;
1854 menu->nScrollPos = 0;
1855 MENU_PopupMenuCalcSize( menu );
1857 /* adjust popup menu pos so that it fits within the desktop */
1859 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1860 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1862 /* FIXME: should use item rect */
1863 pt.x = x;
1864 pt.y = y;
1865 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1866 info.cbSize = sizeof(info);
1867 GetMonitorInfoW( monitor, &info );
1869 if( flags & TPM_RIGHTALIGN ) x -= width;
1870 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1872 if( flags & TPM_BOTTOMALIGN ) y -= height;
1873 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1875 if( x + width > info.rcWork.right)
1877 if( xanchor && x >= width - xanchor )
1878 x -= width - xanchor;
1880 if( x + width > info.rcWork.right)
1881 x = info.rcWork.right - width;
1883 if( x < info.rcWork.left ) x = info.rcWork.left;
1885 if( y + height > info.rcWork.bottom)
1887 if( yanchor && y >= height + yanchor )
1888 y -= height + yanchor;
1890 if( y + height > info.rcWork.bottom)
1891 y = info.rcWork.bottom - height;
1893 if( y < info.rcWork.top ) y = info.rcWork.top;
1895 /* NOTE: In Windows, top menu popup is not owned. */
1896 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1897 WS_POPUP, x, y, width, height,
1898 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1899 (LPVOID)hmenu );
1900 if( !menu->hWnd ) return FALSE;
1901 if (!top_popup) {
1902 top_popup = menu->hWnd;
1903 top_popup_hmenu = hmenu;
1905 /* Display the window */
1907 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1908 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1909 UpdateWindow( menu->hWnd );
1910 return TRUE;
1914 /***********************************************************************
1915 * MENU_EnsureMenuItemVisible
1917 static void
1918 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1920 if (lppop->bScrolling)
1922 MENUITEM *item = &lppop->items[wIndex];
1923 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1924 UINT nOldPos = lppop->nScrollPos;
1925 RECT rc;
1926 UINT arrow_bitmap_height;
1927 BITMAP bmp;
1929 GetClientRect(lppop->hWnd, &rc);
1931 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1932 arrow_bitmap_height = bmp.bmHeight;
1934 rc.top += arrow_bitmap_height;
1935 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1937 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1938 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1941 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1942 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1943 MENU_DrawScrollArrows(lppop, hdc);
1945 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1947 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1948 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1949 MENU_DrawScrollArrows(lppop, hdc);
1955 /***********************************************************************
1956 * MENU_SelectItem
1958 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1959 BOOL sendMenuSelect, HMENU topmenu )
1961 LPPOPUPMENU lppop;
1962 HDC hdc;
1964 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1966 lppop = MENU_GetMenu( hmenu );
1967 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1969 if (lppop->FocusedItem == wIndex) return;
1970 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1971 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1972 if (!top_popup) {
1973 top_popup = lppop->hWnd;
1974 top_popup_hmenu = hmenu;
1977 SelectObject( hdc, get_menu_font(FALSE));
1979 /* Clear previous highlighted item */
1980 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1982 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1983 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1984 lppop->Height, !(lppop->wFlags & MF_POPUP),
1985 ODA_SELECT );
1988 /* Highlight new item (if any) */
1989 lppop->FocusedItem = wIndex;
1990 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1992 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1993 lppop->items[wIndex].fState |= MF_HILITE;
1994 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1995 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1996 &lppop->items[wIndex], lppop->Height,
1997 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1999 if (sendMenuSelect)
2001 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2002 SendMessageW( hwndOwner, WM_MENUSELECT,
2003 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2004 ip->fType | ip->fState |
2005 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2008 else if (sendMenuSelect) {
2009 if(topmenu){
2010 int pos;
2011 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2012 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2013 MENUITEM *ip = &ptm->items[pos];
2014 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2015 ip->fType | ip->fState |
2016 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2020 ReleaseDC( lppop->hWnd, hdc );
2024 /***********************************************************************
2025 * MENU_MoveSelection
2027 * Moves currently selected item according to the offset parameter.
2028 * If there is no selection then it should select the last item if
2029 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2031 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2033 INT i;
2034 POPUPMENU *menu;
2036 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2038 menu = MENU_GetMenu( hmenu );
2039 if ((!menu) || (!menu->items)) return;
2041 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2043 if( menu->nItems == 1 ) return; else
2044 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2045 ; i += offset)
2046 if (!(menu->items[i].fType & MF_SEPARATOR))
2048 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2049 return;
2053 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2054 i >= 0 && i < menu->nItems ; i += offset)
2055 if (!(menu->items[i].fType & MF_SEPARATOR))
2057 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2058 return;
2063 /**********************************************************************
2064 * MENU_InsertItem
2066 * Insert (allocate) a new item into a menu.
2068 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2070 MENUITEM *newItems;
2071 POPUPMENU *menu;
2073 if (!(menu = MENU_GetMenu(hMenu)))
2074 return NULL;
2076 /* Find where to insert new item */
2078 if (flags & MF_BYPOSITION) {
2079 if (pos > menu->nItems)
2080 pos = menu->nItems;
2081 } else {
2082 if (!MENU_FindItem( &hMenu, &pos, flags ))
2083 pos = menu->nItems;
2084 else {
2085 if (!(menu = MENU_GetMenu( hMenu )))
2086 return NULL;
2090 /* Make sure that MDI system buttons stay on the right side.
2091 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2092 * regardless of their id.
2094 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2095 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2096 pos--;
2098 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2100 /* Create new items array */
2102 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2103 if (!newItems)
2105 WARN("allocation failed\n" );
2106 return NULL;
2108 if (menu->nItems > 0)
2110 /* Copy the old array into the new one */
2111 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2112 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2113 (menu->nItems-pos)*sizeof(MENUITEM) );
2114 HeapFree( GetProcessHeap(), 0, menu->items );
2116 menu->items = newItems;
2117 menu->nItems++;
2118 memset( &newItems[pos], 0, sizeof(*newItems) );
2119 menu->Height = 0; /* force size recalculate */
2120 return &newItems[pos];
2124 /**********************************************************************
2125 * MENU_ParseResource
2127 * Parse a standard menu resource and add items to the menu.
2128 * Return a pointer to the end of the resource.
2130 * NOTE: flags is equivalent to the mtOption field
2132 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2134 WORD flags, id = 0;
2135 LPCSTR str;
2136 BOOL end_flag;
2140 flags = GET_WORD(res);
2141 end_flag = flags & MF_END;
2142 /* Remove MF_END because it has the same value as MF_HILITE */
2143 flags &= ~MF_END;
2144 res += sizeof(WORD);
2145 if (!(flags & MF_POPUP))
2147 id = GET_WORD(res);
2148 res += sizeof(WORD);
2150 str = res;
2151 if (!unicode) res += strlen(str) + 1;
2152 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2153 if (flags & MF_POPUP)
2155 HMENU hSubMenu = CreatePopupMenu();
2156 if (!hSubMenu) return NULL;
2157 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2158 return NULL;
2159 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2160 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2162 else /* Not a popup */
2164 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2165 else AppendMenuW( hMenu, flags, id,
2166 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2168 } while (!end_flag);
2169 return res;
2173 /**********************************************************************
2174 * MENUEX_ParseResource
2176 * Parse an extended menu resource and add items to the menu.
2177 * Return a pointer to the end of the resource.
2179 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2181 WORD resinfo;
2182 do {
2183 MENUITEMINFOW mii;
2185 mii.cbSize = sizeof(mii);
2186 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2187 mii.fType = GET_DWORD(res);
2188 res += sizeof(DWORD);
2189 mii.fState = GET_DWORD(res);
2190 res += sizeof(DWORD);
2191 mii.wID = GET_DWORD(res);
2192 res += sizeof(DWORD);
2193 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2194 res += sizeof(WORD);
2195 /* Align the text on a word boundary. */
2196 res += (~((UINT_PTR)res - 1)) & 1;
2197 mii.dwTypeData = (LPWSTR) res;
2198 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2199 /* Align the following fields on a dword boundary. */
2200 res += (~((UINT_PTR)res - 1)) & 3;
2202 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2203 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2205 if (resinfo & 1) { /* Pop-up? */
2206 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2207 res += sizeof(DWORD);
2208 mii.hSubMenu = CreatePopupMenu();
2209 if (!mii.hSubMenu)
2210 return NULL;
2211 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2212 DestroyMenu(mii.hSubMenu);
2213 return NULL;
2215 mii.fMask |= MIIM_SUBMENU;
2216 mii.fType |= MF_POPUP;
2218 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2220 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2221 mii.wID, mii.fType);
2222 mii.fType |= MF_SEPARATOR;
2224 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2225 } while (!(resinfo & MF_END));
2226 return res;
2230 /***********************************************************************
2231 * MENU_GetSubPopup
2233 * Return the handle of the selected sub-popup menu (if any).
2235 static HMENU MENU_GetSubPopup( HMENU hmenu )
2237 POPUPMENU *menu;
2238 MENUITEM *item;
2240 menu = MENU_GetMenu( hmenu );
2242 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2244 item = &menu->items[menu->FocusedItem];
2245 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2246 return item->hSubMenu;
2247 return 0;
2251 /***********************************************************************
2252 * MENU_HideSubPopups
2254 * Hide the sub-popup menus of this menu.
2256 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2257 BOOL sendMenuSelect, UINT wFlags )
2259 POPUPMENU *menu = MENU_GetMenu( hmenu );
2261 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2263 if (menu && top_popup)
2265 HMENU hsubmenu;
2266 POPUPMENU *submenu;
2267 MENUITEM *item;
2269 if (menu->FocusedItem != NO_SELECTED_ITEM)
2271 item = &menu->items[menu->FocusedItem];
2272 if (!(item->fType & MF_POPUP) ||
2273 !(item->fState & MF_MOUSESELECT)) return;
2274 item->fState &= ~MF_MOUSESELECT;
2275 hsubmenu = item->hSubMenu;
2276 } else return;
2278 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2279 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2280 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2281 DestroyWindow( submenu->hWnd );
2282 submenu->hWnd = 0;
2284 if (!(wFlags & TPM_NONOTIFY))
2285 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2286 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2291 /***********************************************************************
2292 * MENU_ShowSubPopup
2294 * Display the sub-menu of the selected item of this menu.
2295 * Return the handle of the submenu, or hmenu if no submenu to display.
2297 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2298 BOOL selectFirst, UINT wFlags )
2300 RECT rect;
2301 POPUPMENU *menu;
2302 MENUITEM *item;
2303 HDC hdc;
2305 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2307 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2309 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2311 item = &menu->items[menu->FocusedItem];
2312 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2313 return hmenu;
2315 /* message must be sent before using item,
2316 because nearly everything may be changed by the application ! */
2318 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2319 if (!(wFlags & TPM_NONOTIFY))
2320 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2321 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2323 item = &menu->items[menu->FocusedItem];
2324 rect = item->rect;
2326 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2327 if (!(item->fState & MF_HILITE))
2329 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2330 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2332 SelectObject( hdc, get_menu_font(FALSE));
2334 item->fState |= MF_HILITE;
2335 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2336 ReleaseDC( menu->hWnd, hdc );
2338 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2339 item->rect = rect;
2341 item->fState |= MF_MOUSESELECT;
2343 if (IS_SYSTEM_MENU(menu))
2345 MENU_InitSysMenuPopup(item->hSubMenu,
2346 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2347 GetClassLongW( menu->hWnd, GCL_STYLE));
2349 NC_GetSysPopupPos( menu->hWnd, &rect );
2350 rect.top = rect.bottom;
2351 rect.right = GetSystemMetrics(SM_CXSIZE);
2352 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2354 else
2356 GetWindowRect( menu->hWnd, &rect );
2357 if (menu->wFlags & MF_POPUP)
2359 RECT rc = item->rect;
2361 MENU_AdjustMenuItemRect(menu, &rc);
2363 /* The first item in the popup menu has to be at the
2364 same y position as the focused menu item */
2365 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2366 rect.top += rc.top - MENU_TOP_MARGIN;
2367 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2368 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2369 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2371 else
2373 rect.left += item->rect.left;
2374 rect.top += item->rect.bottom;
2375 rect.right = item->rect.right - item->rect.left;
2376 rect.bottom = item->rect.bottom - item->rect.top;
2380 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2381 rect.left, rect.top, rect.right, rect.bottom );
2382 if (selectFirst)
2383 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2384 return item->hSubMenu;
2389 /**********************************************************************
2390 * MENU_IsMenuActive
2392 HWND MENU_IsMenuActive(void)
2394 return top_popup;
2397 /**********************************************************************
2398 * MENU_EndMenu
2400 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2402 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2404 void MENU_EndMenu( HWND hwnd )
2406 POPUPMENU *menu;
2407 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2408 if (menu && hwnd == menu->hwndOwner) EndMenu();
2411 /***********************************************************************
2412 * MENU_PtMenu
2414 * Walks menu chain trying to find a menu pt maps to.
2416 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2418 POPUPMENU *menu = MENU_GetMenu( hMenu );
2419 UINT item = menu->FocusedItem;
2420 HMENU ret;
2422 /* try subpopup first (if any) */
2423 ret = (item != NO_SELECTED_ITEM &&
2424 (menu->items[item].fType & MF_POPUP) &&
2425 (menu->items[item].fState & MF_MOUSESELECT))
2426 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2428 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2430 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2431 if( menu->wFlags & MF_POPUP )
2433 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2435 else if (ht == HTSYSMENU)
2436 ret = get_win_sys_menu( menu->hWnd );
2437 else if (ht == HTMENU)
2438 ret = GetMenu( menu->hWnd );
2440 return ret;
2443 /***********************************************************************
2444 * MENU_ExecFocusedItem
2446 * Execute a menu item (for instance when user pressed Enter).
2447 * Return the wID of the executed item. Otherwise, -1 indicating
2448 * that no menu item was executed, -2 if a popup is shown;
2449 * Have to receive the flags for the TrackPopupMenu options to avoid
2450 * sending unwanted message.
2453 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2455 MENUITEM *item;
2456 POPUPMENU *menu = MENU_GetMenu( hMenu );
2458 TRACE("%p hmenu=%p\n", pmt, hMenu);
2460 if (!menu || !menu->nItems ||
2461 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2463 item = &menu->items[menu->FocusedItem];
2465 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2467 if (!(item->fType & MF_POPUP))
2469 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2471 /* If TPM_RETURNCMD is set you return the id, but
2472 do not send a message to the owner */
2473 if(!(wFlags & TPM_RETURNCMD))
2475 if( menu->wFlags & MF_SYSMENU )
2476 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2477 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2478 else
2480 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2481 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2483 if (dwStyle & MNS_NOTIFYBYPOS)
2484 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2485 (LPARAM)hMenu);
2486 else
2487 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2490 return item->wID;
2493 else
2495 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2496 return -2;
2499 return -1;
2502 /***********************************************************************
2503 * MENU_SwitchTracking
2505 * Helper function for menu navigation routines.
2507 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2509 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2510 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2512 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2514 if( pmt->hTopMenu != hPtMenu &&
2515 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2517 /* both are top level menus (system and menu-bar) */
2518 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2519 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2520 pmt->hTopMenu = hPtMenu;
2522 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2523 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2527 /***********************************************************************
2528 * MENU_ButtonDown
2530 * Return TRUE if we can go on with menu tracking.
2532 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2534 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2536 if (hPtMenu)
2538 UINT id = 0;
2539 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2540 MENUITEM *item;
2542 if( IS_SYSTEM_MENU(ptmenu) )
2543 item = ptmenu->items;
2544 else
2545 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2547 if( item )
2549 if( ptmenu->FocusedItem != id )
2550 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2552 /* If the popup menu is not already "popped" */
2553 if(!(item->fState & MF_MOUSESELECT ))
2555 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2558 return TRUE;
2560 /* Else the click was on the menu bar, finish the tracking */
2562 return FALSE;
2565 /***********************************************************************
2566 * MENU_ButtonUp
2568 * Return the value of MENU_ExecFocusedItem if
2569 * the selected item was not a popup. Else open the popup.
2570 * A -1 return value indicates that we go on with menu tracking.
2573 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2575 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2577 if (hPtMenu)
2579 UINT id = 0;
2580 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2581 MENUITEM *item;
2583 if( IS_SYSTEM_MENU(ptmenu) )
2584 item = ptmenu->items;
2585 else
2586 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2588 if( item && (ptmenu->FocusedItem == id ))
2590 debug_print_menuitem ("FocusedItem: ", item, "");
2592 if( !(item->fType & MF_POPUP) )
2594 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2595 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2596 return executedMenuId;
2599 /* If we are dealing with the top-level menu */
2600 /* and this is a click on an already "popped" item: */
2601 /* Stop the menu tracking and close the opened submenus */
2602 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2603 return 0;
2605 ptmenu->bTimeToHide = TRUE;
2607 return -1;
2611 /***********************************************************************
2612 * MENU_MouseMove
2614 * Return TRUE if we can go on with menu tracking.
2616 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2618 UINT id = NO_SELECTED_ITEM;
2619 POPUPMENU *ptmenu = NULL;
2621 if( hPtMenu )
2623 ptmenu = MENU_GetMenu( hPtMenu );
2624 if( IS_SYSTEM_MENU(ptmenu) )
2625 id = 0;
2626 else
2627 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2630 if( id == NO_SELECTED_ITEM )
2632 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2633 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2636 else if( ptmenu->FocusedItem != id )
2638 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2639 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2641 return TRUE;
2645 /***********************************************************************
2646 * MENU_DoNextMenu
2648 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2650 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2652 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2653 BOOL atEnd = FALSE;
2655 /* When skipping left, we need to do something special after the
2656 first menu. */
2657 if (vk == VK_LEFT && menu->FocusedItem == 0)
2659 atEnd = TRUE;
2661 /* When skipping right, for the non-system menu, we need to
2662 handle the last non-special menu item (ie skip any window
2663 icons such as MDI maximize, restore or close) */
2664 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2666 UINT i = menu->FocusedItem + 1;
2667 while (i < menu->nItems) {
2668 if ((menu->items[i].wID >= SC_SIZE &&
2669 menu->items[i].wID <= SC_RESTORE)) {
2670 i++;
2671 } else break;
2673 if (i == menu->nItems) {
2674 atEnd = TRUE;
2677 /* When skipping right, we need to cater for the system menu */
2678 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2680 if (menu->FocusedItem == (menu->nItems - 1)) {
2681 atEnd = TRUE;
2685 if( atEnd )
2687 MDINEXTMENU next_menu;
2688 HMENU hNewMenu;
2689 HWND hNewWnd;
2690 UINT id = 0;
2692 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2693 next_menu.hmenuNext = 0;
2694 next_menu.hwndNext = 0;
2695 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2697 TRACE("%p [%p] -> %p [%p]\n",
2698 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2700 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2702 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2703 hNewWnd = pmt->hOwnerWnd;
2704 if( IS_SYSTEM_MENU(menu) )
2706 /* switch to the menu bar */
2708 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2710 if( vk == VK_LEFT )
2712 menu = MENU_GetMenu( hNewMenu );
2713 id = menu->nItems - 1;
2715 /* Skip backwards over any system predefined icons,
2716 eg. MDI close, restore etc icons */
2717 while ((id > 0) &&
2718 (menu->items[id].wID >= SC_SIZE &&
2719 menu->items[id].wID <= SC_RESTORE)) id--;
2722 else if (style & WS_SYSMENU )
2724 /* switch to the system menu */
2725 hNewMenu = get_win_sys_menu( hNewWnd );
2727 else return FALSE;
2729 else /* application returned a new menu to switch to */
2731 hNewMenu = next_menu.hmenuNext;
2732 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2734 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2736 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2738 if (style & WS_SYSMENU &&
2739 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2741 /* get the real system menu */
2742 hNewMenu = get_win_sys_menu(hNewWnd);
2744 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2746 /* FIXME: Not sure what to do here;
2747 * perhaps try to track hNewMenu as a popup? */
2749 TRACE(" -- got confused.\n");
2750 return FALSE;
2753 else return FALSE;
2756 if( hNewMenu != pmt->hTopMenu )
2758 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2759 FALSE, 0 );
2760 if( pmt->hCurrentMenu != pmt->hTopMenu )
2761 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2764 if( hNewWnd != pmt->hOwnerWnd )
2766 pmt->hOwnerWnd = hNewWnd;
2767 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2770 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2771 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2773 return TRUE;
2775 return FALSE;
2778 /***********************************************************************
2779 * MENU_SuspendPopup
2781 * The idea is not to show the popup if the next input message is
2782 * going to hide it anyway.
2784 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2786 MSG msg;
2788 msg.hwnd = pmt->hOwnerWnd;
2790 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2791 pmt->trackFlags |= TF_SKIPREMOVE;
2793 switch( uMsg )
2795 case WM_KEYDOWN:
2796 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2797 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2799 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2800 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2801 if( msg.message == WM_KEYDOWN &&
2802 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2804 pmt->trackFlags |= TF_SUSPENDPOPUP;
2805 return TRUE;
2808 break;
2811 /* failures go through this */
2812 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2813 return FALSE;
2816 /***********************************************************************
2817 * MENU_KeyEscape
2819 * Handle a VK_ESCAPE key event in a menu.
2821 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2823 BOOL bEndMenu = TRUE;
2825 if (pmt->hCurrentMenu != pmt->hTopMenu)
2827 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2829 if (menu->wFlags & MF_POPUP)
2831 HMENU hmenutmp, hmenuprev;
2833 hmenuprev = hmenutmp = pmt->hTopMenu;
2835 /* close topmost popup */
2836 while (hmenutmp != pmt->hCurrentMenu)
2838 hmenuprev = hmenutmp;
2839 hmenutmp = MENU_GetSubPopup( hmenuprev );
2842 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2843 pmt->hCurrentMenu = hmenuprev;
2844 bEndMenu = FALSE;
2848 return bEndMenu;
2851 /***********************************************************************
2852 * MENU_KeyLeft
2854 * Handle a VK_LEFT key event in a menu.
2856 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2858 POPUPMENU *menu;
2859 HMENU hmenutmp, hmenuprev;
2860 UINT prevcol;
2862 hmenuprev = hmenutmp = pmt->hTopMenu;
2863 menu = MENU_GetMenu( hmenutmp );
2865 /* Try to move 1 column left (if possible) */
2866 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2867 NO_SELECTED_ITEM ) {
2869 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2870 prevcol, TRUE, 0 );
2871 return;
2874 /* close topmost popup */
2875 while (hmenutmp != pmt->hCurrentMenu)
2877 hmenuprev = hmenutmp;
2878 hmenutmp = MENU_GetSubPopup( hmenuprev );
2881 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2882 pmt->hCurrentMenu = hmenuprev;
2884 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2886 /* move menu bar selection if no more popups are left */
2888 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2889 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2891 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2893 /* A sublevel menu was displayed - display the next one
2894 * unless there is another displacement coming up */
2896 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2897 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2898 pmt->hTopMenu, TRUE, wFlags);
2904 /***********************************************************************
2905 * MENU_KeyRight
2907 * Handle a VK_RIGHT key event in a menu.
2909 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2911 HMENU hmenutmp;
2912 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2913 UINT nextcol;
2915 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2916 pmt->hCurrentMenu,
2917 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2918 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2920 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2922 /* If already displaying a popup, try to display sub-popup */
2924 hmenutmp = pmt->hCurrentMenu;
2925 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2927 /* if subpopup was displayed then we are done */
2928 if (hmenutmp != pmt->hCurrentMenu) return;
2931 /* Check to see if there's another column */
2932 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2933 NO_SELECTED_ITEM ) {
2934 TRACE("Going to %d.\n", nextcol );
2935 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2936 nextcol, TRUE, 0 );
2937 return;
2940 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2942 if( pmt->hCurrentMenu != pmt->hTopMenu )
2944 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2945 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2946 } else hmenutmp = 0;
2948 /* try to move to the next item */
2949 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2950 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2952 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2953 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2954 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2955 pmt->hTopMenu, TRUE, wFlags);
2959 static void CALLBACK release_capture( BOOL __normal )
2961 set_capture_window( 0, GUI_INMENUMODE, NULL );
2964 /***********************************************************************
2965 * MENU_TrackMenu
2967 * Menu tracking code.
2969 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2970 HWND hwnd, const RECT *lprect )
2972 MSG msg;
2973 POPUPMENU *menu;
2974 BOOL fRemove;
2975 INT executedMenuId = -1;
2976 MTRACKER mt;
2977 BOOL enterIdleSent = FALSE;
2978 HWND capture_win;
2980 mt.trackFlags = 0;
2981 mt.hCurrentMenu = hmenu;
2982 mt.hTopMenu = hmenu;
2983 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2984 mt.pt.x = x;
2985 mt.pt.y = y;
2987 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2988 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2990 fEndMenu = FALSE;
2991 if (!(menu = MENU_GetMenu( hmenu )))
2993 WARN("Invalid menu handle %p\n", hmenu);
2994 SetLastError(ERROR_INVALID_MENU_HANDLE);
2995 return FALSE;
2998 if (wFlags & TPM_BUTTONDOWN)
3000 /* Get the result in order to start the tracking or not */
3001 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3002 fEndMenu = !fRemove;
3005 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3007 /* owner may not be visible when tracking a popup, so use the menu itself */
3008 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3009 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3011 __TRY while (!fEndMenu)
3013 menu = MENU_GetMenu( mt.hCurrentMenu );
3014 if (!menu) /* sometimes happens if I do a window manager close */
3015 break;
3017 /* we have to keep the message in the queue until it's
3018 * clear that menu loop is not over yet. */
3020 for (;;)
3022 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3024 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3025 /* remove the message from the queue */
3026 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3028 else
3030 if (!enterIdleSent)
3032 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3033 enterIdleSent = TRUE;
3034 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3036 WaitMessage();
3040 /* check if EndMenu() tried to cancel us, by posting this message */
3041 if(msg.message == WM_CANCELMODE)
3043 /* we are now out of the loop */
3044 fEndMenu = TRUE;
3046 /* remove the message from the queue */
3047 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3049 /* break out of internal loop, ala ESCAPE */
3050 break;
3053 TranslateMessage( &msg );
3054 mt.pt = msg.pt;
3056 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3057 enterIdleSent=FALSE;
3059 fRemove = FALSE;
3060 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3063 * Use the mouse coordinates in lParam instead of those in the MSG
3064 * struct to properly handle synthetic messages. They are already
3065 * in screen coordinates.
3067 mt.pt.x = (short)LOWORD(msg.lParam);
3068 mt.pt.y = (short)HIWORD(msg.lParam);
3070 /* Find a menu for this mouse event */
3071 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3073 switch(msg.message)
3075 /* no WM_NC... messages in captured state */
3077 case WM_RBUTTONDBLCLK:
3078 case WM_RBUTTONDOWN:
3079 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3080 /* fall through */
3081 case WM_LBUTTONDBLCLK:
3082 case WM_LBUTTONDOWN:
3083 /* If the message belongs to the menu, removes it from the queue */
3084 /* Else, end menu tracking */
3085 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3086 fEndMenu = !fRemove;
3087 break;
3089 case WM_RBUTTONUP:
3090 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3091 /* fall through */
3092 case WM_LBUTTONUP:
3093 /* Check if a menu was selected by the mouse */
3094 if (hmenu)
3096 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3097 TRACE("executedMenuId %d\n", executedMenuId);
3099 /* End the loop if executedMenuId is an item ID */
3100 /* or if the job was done (executedMenuId = 0). */
3101 fEndMenu = fRemove = (executedMenuId != -1);
3103 /* No menu was selected by the mouse */
3104 /* if the function was called by TrackPopupMenu, continue
3105 with the menu tracking. If not, stop it */
3106 else
3107 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3109 break;
3111 case WM_MOUSEMOVE:
3112 /* the selected menu item must be changed every time */
3113 /* the mouse moves. */
3115 if (hmenu)
3116 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3118 } /* switch(msg.message) - mouse */
3120 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3122 fRemove = TRUE; /* Keyboard messages are always removed */
3123 switch(msg.message)
3125 case WM_KEYDOWN:
3126 case WM_SYSKEYDOWN:
3127 switch(msg.wParam)
3129 case VK_MENU:
3130 fEndMenu = TRUE;
3131 break;
3133 case VK_HOME:
3134 case VK_END:
3135 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3136 NO_SELECTED_ITEM, FALSE, 0 );
3137 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3138 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3139 break;
3141 case VK_UP:
3142 case VK_DOWN: /* If on menu bar, pull-down the menu */
3144 menu = MENU_GetMenu( mt.hCurrentMenu );
3145 if (!(menu->wFlags & MF_POPUP))
3146 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3147 else /* otherwise try to move selection */
3148 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3149 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3150 break;
3152 case VK_LEFT:
3153 MENU_KeyLeft( &mt, wFlags );
3154 break;
3156 case VK_RIGHT:
3157 MENU_KeyRight( &mt, wFlags );
3158 break;
3160 case VK_ESCAPE:
3161 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3162 break;
3164 case VK_F1:
3166 HELPINFO hi;
3167 hi.cbSize = sizeof(HELPINFO);
3168 hi.iContextType = HELPINFO_MENUITEM;
3169 if (menu->FocusedItem == NO_SELECTED_ITEM)
3170 hi.iCtrlId = 0;
3171 else
3172 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3173 hi.hItemHandle = hmenu;
3174 hi.dwContextId = menu->dwContextHelpID;
3175 hi.MousePos = msg.pt;
3176 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3177 break;
3180 default:
3181 break;
3183 break; /* WM_KEYDOWN */
3185 case WM_CHAR:
3186 case WM_SYSCHAR:
3188 UINT pos;
3190 if (msg.wParam == '\r' || msg.wParam == ' ')
3192 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3193 fEndMenu = (executedMenuId != -2);
3195 break;
3198 /* Hack to avoid control chars. */
3199 /* We will find a better way real soon... */
3200 if (msg.wParam < 32) break;
3202 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3203 LOWORD(msg.wParam), FALSE );
3204 if (pos == (UINT)-2) fEndMenu = TRUE;
3205 else if (pos == (UINT)-1) MessageBeep(0);
3206 else
3208 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3209 TRUE, 0 );
3210 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3211 fEndMenu = (executedMenuId != -2);
3214 break;
3215 } /* switch(msg.message) - kbd */
3217 else
3219 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3220 DispatchMessageW( &msg );
3221 continue;
3224 if (!fEndMenu) fRemove = TRUE;
3226 /* finally remove message from the queue */
3228 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3229 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3230 else mt.trackFlags &= ~TF_SKIPREMOVE;
3232 __FINALLY( release_capture )
3234 /* If dropdown is still painted and the close box is clicked on
3235 then the menu will be destroyed as part of the DispatchMessage above.
3236 This will then invalidate the menu handle in mt.hTopMenu. We should
3237 check for this first. */
3238 if( IsMenu( mt.hTopMenu ) )
3240 menu = MENU_GetMenu( mt.hTopMenu );
3242 if( IsWindow( mt.hOwnerWnd ) )
3244 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3246 if (menu && (menu->wFlags & MF_POPUP))
3248 DestroyWindow( menu->hWnd );
3249 menu->hWnd = 0;
3251 if (!(wFlags & TPM_NONOTIFY))
3252 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3253 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3255 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3256 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3259 /* Reset the variable for hiding menu */
3260 if( menu ) menu->bTimeToHide = FALSE;
3263 /* The return value is only used by TrackPopupMenu */
3264 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3265 if (executedMenuId == -1) executedMenuId = 0;
3266 return executedMenuId;
3269 /***********************************************************************
3270 * MENU_InitTracking
3272 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3274 POPUPMENU *menu;
3276 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3278 HideCaret(0);
3280 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3281 if (!(wFlags & TPM_NONOTIFY))
3282 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3284 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3286 if (!(wFlags & TPM_NONOTIFY))
3288 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3289 /* If an app changed/recreated menu bar entries in WM_INITMENU
3290 * menu sizes will be recalculated once the menu created/shown.
3294 /* This makes the menus of applications built with Delphi work.
3295 * It also enables menus to be displayed in more than one window,
3296 * but there are some bugs left that need to be fixed in this case.
3298 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3300 return TRUE;
3302 /***********************************************************************
3303 * MENU_ExitTracking
3305 static BOOL MENU_ExitTracking(HWND hWnd)
3307 TRACE("hwnd=%p\n", hWnd);
3309 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3310 ShowCaret(0);
3311 top_popup = 0;
3312 top_popup_hmenu = NULL;
3313 return TRUE;
3316 /***********************************************************************
3317 * MENU_TrackMouseMenuBar
3319 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3321 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3323 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3324 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3326 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3328 if (IsMenu(hMenu))
3330 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3331 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3332 MENU_ExitTracking(hWnd);
3337 /***********************************************************************
3338 * MENU_TrackKbdMenuBar
3340 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3342 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3344 UINT uItem = NO_SELECTED_ITEM;
3345 HMENU hTrackMenu;
3346 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3348 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3350 /* find window that has a menu */
3352 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3353 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3355 /* check if we have to track a system menu */
3357 hTrackMenu = GetMenu( hwnd );
3358 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3360 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3361 hTrackMenu = get_win_sys_menu( hwnd );
3362 uItem = 0;
3363 wParam |= HTSYSMENU; /* prevent item lookup */
3366 if (!IsMenu( hTrackMenu )) return;
3368 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3370 if( wChar && wChar != ' ' )
3372 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3373 if ( uItem >= (UINT)(-2) )
3375 if( uItem == (UINT)(-1) ) MessageBeep(0);
3376 /* schedule end of menu tracking */
3377 wFlags |= TF_ENDMENU;
3378 goto track_menu;
3382 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3384 if (!(wParam & HTSYSMENU) || wChar == ' ')
3386 if( uItem == NO_SELECTED_ITEM )
3387 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3388 else
3389 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3392 track_menu:
3393 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3394 MENU_ExitTracking( hwnd );
3397 /**********************************************************************
3398 * TrackPopupMenuEx (USER32.@)
3400 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3401 HWND hWnd, LPTPMPARAMS lpTpm )
3403 BOOL ret = FALSE;
3405 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3406 hMenu, wFlags, x, y, hWnd, lpTpm,
3407 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3409 /* Parameter check */
3410 /* FIXME: this check is performed several times, here and in the called
3411 functions. That could be optimized */
3412 if (!MENU_GetMenu( hMenu ))
3414 SetLastError( ERROR_INVALID_MENU_HANDLE );
3415 return FALSE;
3418 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3420 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3421 if (!(wFlags & TPM_NONOTIFY))
3422 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3424 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3425 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3426 lpTpm ? &lpTpm->rcExclude : NULL );
3427 MENU_ExitTracking(hWnd);
3429 return ret;
3432 /**********************************************************************
3433 * TrackPopupMenu (USER32.@)
3435 * Like the win32 API, the function return the command ID only if the
3436 * flag TPM_RETURNCMD is on.
3439 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3440 INT nReserved, HWND hWnd, const RECT *lpRect )
3442 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3445 /***********************************************************************
3446 * PopupMenuWndProc
3448 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3450 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3452 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3454 switch(message)
3456 case WM_CREATE:
3458 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3459 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3460 return 0;
3463 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3464 return MA_NOACTIVATE;
3466 case WM_PAINT:
3468 PAINTSTRUCT ps;
3469 BeginPaint( hwnd, &ps );
3470 MENU_DrawPopupMenu( hwnd, ps.hdc,
3471 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3472 EndPaint( hwnd, &ps );
3473 return 0;
3476 case WM_PRINTCLIENT:
3478 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3479 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3480 return 0;
3483 case WM_ERASEBKGND:
3484 return 1;
3486 case WM_DESTROY:
3487 /* zero out global pointer in case resident popup window was destroyed. */
3488 if (hwnd == top_popup) {
3489 top_popup = 0;
3490 top_popup_hmenu = NULL;
3492 break;
3494 case WM_SHOWWINDOW:
3496 if( wParam )
3498 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3500 else
3501 SetWindowLongPtrW( hwnd, 0, 0 );
3502 break;
3504 case MM_SETMENUHANDLE:
3505 SetWindowLongPtrW( hwnd, 0, wParam );
3506 break;
3508 case MM_GETMENUHANDLE:
3509 return GetWindowLongPtrW( hwnd, 0 );
3511 default:
3512 return DefWindowProcW( hwnd, message, wParam, lParam );
3514 return 0;
3518 /***********************************************************************
3519 * MENU_GetMenuBarHeight
3521 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3523 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3524 INT orgX, INT orgY )
3526 HDC hdc;
3527 RECT rectBar;
3528 LPPOPUPMENU lppop;
3530 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3532 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3534 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3535 SelectObject( hdc, get_menu_font(FALSE));
3536 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3537 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3538 ReleaseDC( hwnd, hdc );
3539 return lppop->Height;
3543 /*******************************************************************
3544 * ChangeMenuA (USER32.@)
3546 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3547 UINT id, UINT flags )
3549 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3550 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3551 id, data );
3552 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3553 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3554 id, data );
3555 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3556 flags & MF_BYPOSITION ? pos : id,
3557 flags & ~MF_REMOVE );
3558 /* Default: MF_INSERT */
3559 return InsertMenuA( hMenu, pos, flags, id, data );
3563 /*******************************************************************
3564 * ChangeMenuW (USER32.@)
3566 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3567 UINT id, UINT flags )
3569 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3570 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3571 id, data );
3572 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3573 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3574 id, data );
3575 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3576 flags & MF_BYPOSITION ? pos : id,
3577 flags & ~MF_REMOVE );
3578 /* Default: MF_INSERT */
3579 return InsertMenuW( hMenu, pos, flags, id, data );
3583 /*******************************************************************
3584 * CheckMenuItem (USER32.@)
3586 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3588 MENUITEM *item;
3589 DWORD ret;
3591 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3592 ret = item->fState & MF_CHECKED;
3593 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3594 else item->fState &= ~MF_CHECKED;
3595 return ret;
3599 /**********************************************************************
3600 * EnableMenuItem (USER32.@)
3602 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3604 UINT oldflags;
3605 MENUITEM *item;
3606 POPUPMENU *menu;
3608 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3610 /* Get the Popupmenu to access the owner menu */
3611 if (!(menu = MENU_GetMenu(hMenu)))
3612 return (UINT)-1;
3614 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3615 return (UINT)-1;
3617 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3618 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3620 /* If the close item in the system menu change update the close button */
3621 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3623 if (menu->hSysMenuOwner != 0)
3625 RECT rc;
3626 POPUPMENU* parentMenu;
3628 /* Get the parent menu to access*/
3629 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3630 return (UINT)-1;
3632 /* Refresh the frame to reflect the change */
3633 GetWindowRect(parentMenu->hWnd, &rc);
3634 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3635 rc.bottom = 0;
3636 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3640 return oldflags;
3644 /*******************************************************************
3645 * GetMenuStringA (USER32.@)
3647 INT WINAPI GetMenuStringA(
3648 HMENU hMenu, /* [in] menuhandle */
3649 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3650 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3651 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3652 UINT wFlags /* [in] MF_ flags */
3654 MENUITEM *item;
3656 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3657 if (str && nMaxSiz) str[0] = '\0';
3658 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3659 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3660 return 0;
3662 if (!item->text) return 0;
3663 if (!str || !nMaxSiz) return strlenW(item->text);
3664 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3665 str[nMaxSiz-1] = 0;
3666 TRACE("returning %s\n", debugstr_a(str));
3667 return strlen(str);
3671 /*******************************************************************
3672 * GetMenuStringW (USER32.@)
3674 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3675 LPWSTR str, INT nMaxSiz, UINT wFlags )
3677 MENUITEM *item;
3679 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3680 if (str && nMaxSiz) str[0] = '\0';
3681 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3682 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3683 return 0;
3685 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3686 if( !(item->text)) {
3687 str[0] = 0;
3688 return 0;
3690 lstrcpynW( str, item->text, nMaxSiz );
3691 TRACE("returning %s\n", debugstr_w(str));
3692 return strlenW(str);
3696 /**********************************************************************
3697 * HiliteMenuItem (USER32.@)
3699 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3700 UINT wHilite )
3702 LPPOPUPMENU menu;
3703 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3704 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3705 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3706 if (menu->FocusedItem == wItemID) return TRUE;
3707 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3708 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3709 return TRUE;
3713 /**********************************************************************
3714 * GetMenuState (USER32.@)
3716 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3718 MENUITEM *item;
3719 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3720 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3721 debug_print_menuitem (" item: ", item, "");
3722 if (item->fType & MF_POPUP)
3724 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3725 if (!menu) return -1;
3726 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3728 else
3730 /* We used to (from way back then) mask the result to 0xff. */
3731 /* I don't know why and it seems wrong as the documented */
3732 /* return flag MF_SEPARATOR is outside that mask. */
3733 return (item->fType | item->fState);
3738 /**********************************************************************
3739 * GetMenuItemCount (USER32.@)
3741 INT WINAPI GetMenuItemCount( HMENU hMenu )
3743 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3744 if (!menu) return -1;
3745 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3746 return menu->nItems;
3750 /**********************************************************************
3751 * GetMenuItemID (USER32.@)
3753 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3755 MENUITEM * lpmi;
3757 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3758 if (lpmi->fType & MF_POPUP) return -1;
3759 return lpmi->wID;
3764 /**********************************************************************
3765 * MENU_mnu2mnuii
3767 * Uses flags, id and text ptr, passed by InsertMenu() and
3768 * ModifyMenu() to setup a MenuItemInfo structure.
3770 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3771 LPMENUITEMINFOW pmii)
3773 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3774 pmii->cbSize = sizeof( MENUITEMINFOW);
3775 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3776 /* setting bitmap clears text and vice versa */
3777 if( IS_STRING_ITEM(flags)) {
3778 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3779 if( !str)
3780 flags |= MF_SEPARATOR;
3781 /* Item beginning with a backspace is a help item */
3782 /* FIXME: wrong place, this is only true in win16 */
3783 else if( *str == '\b') {
3784 flags |= MF_HELP;
3785 str++;
3787 pmii->dwTypeData = (LPWSTR)str;
3788 } else if( flags & MFT_BITMAP){
3789 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3790 pmii->hbmpItem = HBITMAP_32(LOWORD(str));
3792 if( flags & MF_OWNERDRAW){
3793 pmii->fMask |= MIIM_DATA;
3794 pmii->dwItemData = (ULONG_PTR) str;
3796 if( flags & MF_POPUP) {
3797 pmii->fMask |= MIIM_SUBMENU;
3798 pmii->hSubMenu = (HMENU)id;
3800 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3801 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3802 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3803 pmii->wID = (UINT)id;
3807 /*******************************************************************
3808 * InsertMenuW (USER32.@)
3810 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3811 UINT_PTR id, LPCWSTR str )
3813 MENUITEM *item;
3814 MENUITEMINFOW mii;
3816 if (IS_STRING_ITEM(flags) && str)
3817 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3818 hMenu, pos, flags, id, debugstr_w(str) );
3819 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3820 hMenu, pos, flags, id, str );
3822 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3823 MENU_mnu2mnuii( flags, id, str, &mii);
3824 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3826 RemoveMenu( hMenu, pos, flags );
3827 return FALSE;
3830 item->hCheckBit = item->hUnCheckBit = 0;
3831 return TRUE;
3835 /*******************************************************************
3836 * InsertMenuA (USER32.@)
3838 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3839 UINT_PTR id, LPCSTR str )
3841 BOOL ret = FALSE;
3843 if (IS_STRING_ITEM(flags) && str)
3845 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3846 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3847 if (newstr)
3849 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3850 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3851 HeapFree( GetProcessHeap(), 0, newstr );
3853 return ret;
3855 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3859 /*******************************************************************
3860 * AppendMenuA (USER32.@)
3862 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3863 UINT_PTR id, LPCSTR data )
3865 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3869 /*******************************************************************
3870 * AppendMenuW (USER32.@)
3872 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3873 UINT_PTR id, LPCWSTR data )
3875 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3879 /**********************************************************************
3880 * RemoveMenu (USER32.@)
3882 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3884 LPPOPUPMENU menu;
3885 MENUITEM *item;
3887 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3888 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3889 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3891 /* Remove item */
3893 MENU_FreeItemData( item );
3895 if (--menu->nItems == 0)
3897 HeapFree( GetProcessHeap(), 0, menu->items );
3898 menu->items = NULL;
3900 else
3902 while(nPos < menu->nItems)
3904 *item = *(item+1);
3905 item++;
3906 nPos++;
3908 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3909 menu->nItems * sizeof(MENUITEM) );
3911 return TRUE;
3915 /**********************************************************************
3916 * DeleteMenu (USER32.@)
3918 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3920 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3921 if (!item) return FALSE;
3922 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3923 /* nPos is now the position of the item */
3924 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3925 return TRUE;
3929 /*******************************************************************
3930 * ModifyMenuW (USER32.@)
3932 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3933 UINT_PTR id, LPCWSTR str )
3935 MENUITEM *item;
3936 MENUITEMINFOW mii;
3938 if (IS_STRING_ITEM(flags))
3939 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3940 else
3941 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3943 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3944 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3945 MENU_mnu2mnuii( flags, id, str, &mii);
3946 return SetMenuItemInfo_common( item, &mii, TRUE);
3950 /*******************************************************************
3951 * ModifyMenuA (USER32.@)
3953 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3954 UINT_PTR id, LPCSTR str )
3956 BOOL ret = FALSE;
3958 if (IS_STRING_ITEM(flags) && str)
3960 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3961 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3962 if (newstr)
3964 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3965 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3966 HeapFree( GetProcessHeap(), 0, newstr );
3968 return ret;
3970 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3974 /**********************************************************************
3975 * CreatePopupMenu (USER32.@)
3977 HMENU WINAPI CreatePopupMenu(void)
3979 HMENU hmenu;
3980 POPUPMENU *menu;
3982 if (!(hmenu = CreateMenu())) return 0;
3983 menu = MENU_GetMenu( hmenu );
3984 menu->wFlags |= MF_POPUP;
3985 menu->bTimeToHide = FALSE;
3986 return hmenu;
3990 /**********************************************************************
3991 * GetMenuCheckMarkDimensions (USER.417)
3992 * GetMenuCheckMarkDimensions (USER32.@)
3994 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3996 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4000 /**********************************************************************
4001 * SetMenuItemBitmaps (USER32.@)
4003 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4004 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4006 MENUITEM *item;
4008 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4010 if (!hNewCheck && !hNewUnCheck)
4012 item->fState &= ~MF_USECHECKBITMAPS;
4014 else /* Install new bitmaps */
4016 item->hCheckBit = hNewCheck;
4017 item->hUnCheckBit = hNewUnCheck;
4018 item->fState |= MF_USECHECKBITMAPS;
4020 return TRUE;
4024 /**********************************************************************
4025 * CreateMenu (USER32.@)
4027 HMENU WINAPI CreateMenu(void)
4029 HMENU hMenu;
4030 LPPOPUPMENU menu;
4031 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4032 menu = USER_HEAP_LIN_ADDR(hMenu);
4034 ZeroMemory(menu, sizeof(POPUPMENU));
4035 menu->wMagic = MENU_MAGIC;
4036 menu->FocusedItem = NO_SELECTED_ITEM;
4037 menu->bTimeToHide = FALSE;
4039 TRACE("return %p\n", hMenu );
4041 return hMenu;
4045 /**********************************************************************
4046 * DestroyMenu (USER32.@)
4048 BOOL WINAPI DestroyMenu( HMENU hMenu )
4050 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4052 TRACE("(%p)\n", hMenu);
4055 if (!lppop) return FALSE;
4057 lppop->wMagic = 0; /* Mark it as destroyed */
4059 /* DestroyMenu should not destroy system menu popup owner */
4060 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4062 DestroyWindow( lppop->hWnd );
4063 lppop->hWnd = 0;
4066 if (lppop->items) /* recursively destroy submenus */
4068 int i;
4069 MENUITEM *item = lppop->items;
4070 for (i = lppop->nItems; i > 0; i--, item++)
4072 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4073 MENU_FreeItemData( item );
4075 HeapFree( GetProcessHeap(), 0, lppop->items );
4077 USER_HEAP_FREE( hMenu );
4078 return TRUE;
4082 /**********************************************************************
4083 * GetSystemMenu (USER32.@)
4085 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4087 WND *wndPtr = WIN_GetPtr( hWnd );
4088 HMENU retvalue = 0;
4090 if (wndPtr == WND_DESKTOP) return 0;
4091 if (wndPtr == WND_OTHER_PROCESS)
4093 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4095 else if (wndPtr)
4097 if (wndPtr->hSysMenu && bRevert)
4099 DestroyMenu(wndPtr->hSysMenu);
4100 wndPtr->hSysMenu = 0;
4103 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4104 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4106 if( wndPtr->hSysMenu )
4108 POPUPMENU *menu;
4109 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4111 /* Store the dummy sysmenu handle to facilitate the refresh */
4112 /* of the close button if the SC_CLOSE item change */
4113 menu = MENU_GetMenu(retvalue);
4114 if ( menu )
4115 menu->hSysMenuOwner = wndPtr->hSysMenu;
4117 WIN_ReleasePtr( wndPtr );
4119 return bRevert ? 0 : retvalue;
4123 /*******************************************************************
4124 * SetSystemMenu (USER32.@)
4126 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4128 WND *wndPtr = WIN_GetPtr( hwnd );
4130 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4132 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4133 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4134 WIN_ReleasePtr( wndPtr );
4135 return TRUE;
4137 return FALSE;
4141 /**********************************************************************
4142 * GetMenu (USER32.@)
4144 HMENU WINAPI GetMenu( HWND hWnd )
4146 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4147 TRACE("for %p returning %p\n", hWnd, retvalue);
4148 return retvalue;
4151 /**********************************************************************
4152 * GetMenuBarInfo (USER32.@)
4154 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4156 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4157 return FALSE;
4160 /**********************************************************************
4161 * MENU_SetMenu
4163 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4164 * SetWindowPos call that would result if SetMenu were called directly.
4166 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4168 TRACE("(%p, %p);\n", hWnd, hMenu);
4170 if (hMenu && !IsMenu(hMenu))
4172 WARN("hMenu %p is not a menu handle\n", hMenu);
4173 return FALSE;
4175 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4176 return FALSE;
4178 hWnd = WIN_GetFullHandle( hWnd );
4179 if (GetCapture() == hWnd)
4180 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4182 if (hMenu != 0)
4184 LPPOPUPMENU lpmenu;
4186 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4188 lpmenu->hWnd = hWnd;
4189 lpmenu->Height = 0; /* Make sure we recalculate the size */
4191 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4192 return TRUE;
4196 /**********************************************************************
4197 * SetMenu (USER32.@)
4199 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4201 if(!MENU_SetMenu(hWnd, hMenu))
4202 return FALSE;
4204 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4205 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4206 return TRUE;
4210 /**********************************************************************
4211 * GetSubMenu (USER32.@)
4213 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4215 MENUITEM * lpmi;
4217 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4218 if (!(lpmi->fType & MF_POPUP)) return 0;
4219 return lpmi->hSubMenu;
4223 /**********************************************************************
4224 * DrawMenuBar (USER32.@)
4226 BOOL WINAPI DrawMenuBar( HWND hWnd )
4228 LPPOPUPMENU lppop;
4229 HMENU hMenu = GetMenu(hWnd);
4231 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4232 return FALSE;
4233 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4235 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4236 lppop->hwndOwner = hWnd;
4237 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4238 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4239 return TRUE;
4242 /***********************************************************************
4243 * DrawMenuBarTemp (USER32.@)
4245 * UNDOCUMENTED !!
4247 * called by W98SE desk.cpl Control Panel Applet
4249 * Not 100% sure about the param names, but close.
4251 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4253 LPPOPUPMENU lppop;
4254 UINT i,retvalue;
4255 HFONT hfontOld = 0;
4256 BOOL flat_menu = FALSE;
4258 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4260 if (!hMenu)
4261 hMenu = GetMenu(hwnd);
4263 if (!hFont)
4264 hFont = get_menu_font(FALSE);
4266 lppop = MENU_GetMenu( hMenu );
4267 if (lppop == NULL || lprect == NULL)
4269 retvalue = GetSystemMetrics(SM_CYMENU);
4270 goto END;
4273 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4275 hfontOld = SelectObject( hDC, hFont);
4277 if (lppop->Height == 0)
4278 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4280 lprect->bottom = lprect->top + lppop->Height;
4282 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4284 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4285 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4286 LineTo( hDC, lprect->right, lprect->bottom );
4288 if (lppop->nItems == 0)
4290 retvalue = GetSystemMetrics(SM_CYMENU);
4291 goto END;
4294 for (i = 0; i < lppop->nItems; i++)
4296 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4297 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4299 retvalue = lppop->Height;
4301 END:
4302 if (hfontOld) SelectObject (hDC, hfontOld);
4303 return retvalue;
4306 /***********************************************************************
4307 * EndMenu (USER.187)
4308 * EndMenu (USER32.@)
4310 BOOL WINAPI EndMenu(void)
4312 /* if we are in the menu code, and it is active */
4313 if (!fEndMenu && top_popup)
4315 /* terminate the menu handling code */
4316 fEndMenu = TRUE;
4318 /* needs to be posted to wakeup the internal menu handler */
4319 /* which will now terminate the menu, in the event that */
4320 /* the main window was minimized, or lost focus, so we */
4321 /* don't end up with an orphaned menu */
4322 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4324 return fEndMenu;
4328 /***********************************************************************
4329 * LookupMenuHandle (USER.217)
4331 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4333 HMENU hmenu32 = HMENU_32(hmenu);
4334 UINT id32 = id;
4335 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4336 else return HMENU_16(hmenu32);
4340 /**********************************************************************
4341 * LoadMenu (USER.150)
4343 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4345 HRSRC16 hRsrc;
4346 HGLOBAL16 handle;
4347 HMENU16 hMenu;
4349 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4350 if (!name) return 0;
4352 instance = GetExePtr( instance );
4353 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4354 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4355 hMenu = LoadMenuIndirect16(LockResource16(handle));
4356 FreeResource16( handle );
4357 return hMenu;
4361 /*****************************************************************
4362 * LoadMenuA (USER32.@)
4364 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4366 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4367 if (!hrsrc) return 0;
4368 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4372 /*****************************************************************
4373 * LoadMenuW (USER32.@)
4375 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4377 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4378 if (!hrsrc) return 0;
4379 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4383 /**********************************************************************
4384 * LoadMenuIndirect (USER.220)
4386 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4388 HMENU hMenu;
4389 WORD version, offset;
4390 LPCSTR p = template;
4392 TRACE("(%p)\n", template );
4393 version = GET_WORD(p);
4394 p += sizeof(WORD);
4395 if (version)
4397 WARN("version must be 0 for Win16\n" );
4398 return 0;
4400 offset = GET_WORD(p);
4401 p += sizeof(WORD) + offset;
4402 if (!(hMenu = CreateMenu())) return 0;
4403 if (!MENU_ParseResource( p, hMenu, FALSE ))
4405 DestroyMenu( hMenu );
4406 return 0;
4408 return HMENU_16(hMenu);
4412 /**********************************************************************
4413 * LoadMenuIndirectW (USER32.@)
4415 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4417 HMENU hMenu;
4418 WORD version, offset;
4419 LPCSTR p = template;
4421 version = GET_WORD(p);
4422 p += sizeof(WORD);
4423 TRACE("%p, ver %d\n", template, version );
4424 switch (version)
4426 case 0: /* standard format is version of 0 */
4427 offset = GET_WORD(p);
4428 p += sizeof(WORD) + offset;
4429 if (!(hMenu = CreateMenu())) return 0;
4430 if (!MENU_ParseResource( p, hMenu, TRUE ))
4432 DestroyMenu( hMenu );
4433 return 0;
4435 return hMenu;
4436 case 1: /* extended format is version of 1 */
4437 offset = GET_WORD(p);
4438 p += sizeof(WORD) + offset;
4439 if (!(hMenu = CreateMenu())) return 0;
4440 if (!MENUEX_ParseResource( p, hMenu))
4442 DestroyMenu( hMenu );
4443 return 0;
4445 return hMenu;
4446 default:
4447 ERR("version %d not supported.\n", version);
4448 return 0;
4453 /**********************************************************************
4454 * LoadMenuIndirectA (USER32.@)
4456 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4458 return LoadMenuIndirectW( template );
4462 /**********************************************************************
4463 * IsMenu (USER32.@)
4465 BOOL WINAPI IsMenu(HMENU hmenu)
4467 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4469 if (!menu)
4471 SetLastError(ERROR_INVALID_MENU_HANDLE);
4472 return FALSE;
4474 return TRUE;
4477 /**********************************************************************
4478 * GetMenuItemInfo_common
4481 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4482 LPMENUITEMINFOW lpmii, BOOL unicode)
4484 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4486 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4488 if (!menu) {
4489 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4490 return FALSE;
4493 if( lpmii->fMask & MIIM_TYPE) {
4494 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4495 WARN("invalid combination of fMask bits used\n");
4496 /* this does not happen on Win9x/ME */
4497 SetLastError( ERROR_INVALID_PARAMETER);
4498 return FALSE;
4500 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4501 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4502 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4503 if( lpmii->fType & MFT_BITMAP) {
4504 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4505 lpmii->cch = 0;
4506 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4507 /* this does not happen on Win9x/ME */
4508 lpmii->dwTypeData = 0;
4509 lpmii->cch = 0;
4513 /* copy the text string */
4514 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4515 if( !menu->text ) {
4516 if(lpmii->dwTypeData && lpmii->cch) {
4517 lpmii->cch = 0;
4518 if( unicode)
4519 *((WCHAR *)lpmii->dwTypeData) = 0;
4520 else
4521 *((CHAR *)lpmii->dwTypeData) = 0;
4523 } else {
4524 int len;
4525 if (unicode)
4527 len = strlenW(menu->text);
4528 if(lpmii->dwTypeData && lpmii->cch)
4529 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4531 else
4533 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4534 0, NULL, NULL ) - 1;
4535 if(lpmii->dwTypeData && lpmii->cch)
4536 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4537 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4538 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4540 /* if we've copied a substring we return its length */
4541 if(lpmii->dwTypeData && lpmii->cch)
4542 if (lpmii->cch <= len + 1)
4543 lpmii->cch--;
4544 else
4545 lpmii->cch = len;
4546 else {
4547 /* return length of string */
4548 /* not on Win9x/ME if fType & MFT_BITMAP */
4549 lpmii->cch = len;
4554 if (lpmii->fMask & MIIM_FTYPE)
4555 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4557 if (lpmii->fMask & MIIM_BITMAP)
4558 lpmii->hbmpItem = menu->hbmpItem;
4560 if (lpmii->fMask & MIIM_STATE)
4561 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4563 if (lpmii->fMask & MIIM_ID)
4564 lpmii->wID = menu->wID;
4566 if (lpmii->fMask & MIIM_SUBMENU)
4567 lpmii->hSubMenu = menu->hSubMenu;
4568 else {
4569 /* hSubMenu is always cleared
4570 * (not on Win9x/ME ) */
4571 lpmii->hSubMenu = 0;
4574 if (lpmii->fMask & MIIM_CHECKMARKS) {
4575 lpmii->hbmpChecked = menu->hCheckBit;
4576 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4578 if (lpmii->fMask & MIIM_DATA)
4579 lpmii->dwItemData = menu->dwItemData;
4581 return TRUE;
4584 /**********************************************************************
4585 * GetMenuItemInfoA (USER32.@)
4587 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4588 LPMENUITEMINFOA lpmii)
4590 BOOL ret;
4591 MENUITEMINFOA mii;
4592 if( lpmii->cbSize != sizeof( mii) &&
4593 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4594 SetLastError( ERROR_INVALID_PARAMETER);
4595 return FALSE;
4597 memcpy( &mii, lpmii, lpmii->cbSize);
4598 mii.cbSize = sizeof( mii);
4599 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4600 (LPMENUITEMINFOW)&mii, FALSE);
4601 mii.cbSize = lpmii->cbSize;
4602 memcpy( lpmii, &mii, mii.cbSize);
4603 return ret;
4606 /**********************************************************************
4607 * GetMenuItemInfoW (USER32.@)
4609 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4610 LPMENUITEMINFOW lpmii)
4612 BOOL ret;
4613 MENUITEMINFOW mii;
4614 if( lpmii->cbSize != sizeof( mii) &&
4615 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4616 SetLastError( ERROR_INVALID_PARAMETER);
4617 return FALSE;
4619 memcpy( &mii, lpmii, lpmii->cbSize);
4620 mii.cbSize = sizeof( mii);
4621 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4622 mii.cbSize = lpmii->cbSize;
4623 memcpy( lpmii, &mii, mii.cbSize);
4624 return ret;
4628 /* set a menu item text from a ASCII or Unicode string */
4629 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4631 if (!text)
4632 menu->text = NULL;
4633 else if (unicode)
4635 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4636 strcpyW( menu->text, text );
4638 else
4640 LPCSTR str = (LPCSTR)text;
4641 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4642 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4643 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4648 /**********************************************************************
4649 * MENU_depth
4651 * detect if there are loops in the menu tree (or the depth is too large)
4653 static int MENU_depth( POPUPMENU *pmenu, int depth)
4655 int i;
4656 MENUITEM *item;
4657 int subdepth;
4659 depth++;
4660 if( depth > MAXMENUDEPTH) return depth;
4661 item = pmenu->items;
4662 subdepth = depth;
4663 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4664 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4665 if( psubmenu){
4666 int bdepth = MENU_depth( psubmenu, depth);
4667 if( bdepth > subdepth) subdepth = bdepth;
4669 if( subdepth > MAXMENUDEPTH)
4670 TRACE("<- hmenu %p\n", item->hSubMenu);
4672 return subdepth;
4676 /**********************************************************************
4677 * SetMenuItemInfo_common
4679 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4680 * MIIM_BITMAP and MIIM_STRING flags instead.
4683 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4684 const MENUITEMINFOW *lpmii,
4685 BOOL unicode)
4687 if (!menu) return FALSE;
4689 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4691 if (lpmii->fMask & MIIM_FTYPE ) {
4692 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4693 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4695 if (lpmii->fMask & MIIM_STRING ) {
4696 /* free the string when used */
4697 HeapFree(GetProcessHeap(), 0, menu->text);
4698 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4701 if (lpmii->fMask & MIIM_STATE)
4702 /* Other menu items having MFS_DEFAULT are not converted
4703 to normal items */
4704 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4706 if (lpmii->fMask & MIIM_ID)
4707 menu->wID = lpmii->wID;
4709 if (lpmii->fMask & MIIM_SUBMENU) {
4710 menu->hSubMenu = lpmii->hSubMenu;
4711 if (menu->hSubMenu) {
4712 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4713 if (subMenu) {
4714 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4715 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4716 menu->hSubMenu = 0;
4717 return FALSE;
4719 subMenu->wFlags |= MF_POPUP;
4720 menu->fType |= MF_POPUP;
4721 } else {
4722 SetLastError( ERROR_INVALID_PARAMETER);
4723 return FALSE;
4726 else
4727 menu->fType &= ~MF_POPUP;
4730 if (lpmii->fMask & MIIM_CHECKMARKS)
4732 menu->hCheckBit = lpmii->hbmpChecked;
4733 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4735 if (lpmii->fMask & MIIM_DATA)
4736 menu->dwItemData = lpmii->dwItemData;
4738 if (lpmii->fMask & MIIM_BITMAP)
4739 menu->hbmpItem = lpmii->hbmpItem;
4741 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4742 menu->fType |= MFT_SEPARATOR;
4744 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4745 return TRUE;
4748 /**********************************************************************
4749 * MENU_NormalizeMenuItemInfoStruct
4751 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4752 * check, copy and extend the MENUITEMINFO struct from the version that the application
4753 * supplied to the version used by wine source. */
4754 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4755 MENUITEMINFOW *pmii_out )
4757 /* do we recognize the size? */
4758 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4759 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4760 SetLastError( ERROR_INVALID_PARAMETER);
4761 return FALSE;
4763 /* copy the fields that we have */
4764 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4765 /* if the hbmpItem member is missing then extend */
4766 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4767 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4768 pmii_out->hbmpItem = NULL;
4770 /* test for invalid bit combinations */
4771 if( (pmii_out->fMask & MIIM_TYPE &&
4772 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4773 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4774 WARN("invalid combination of fMask bits used\n");
4775 /* this does not happen on Win9x/ME */
4776 SetLastError( ERROR_INVALID_PARAMETER);
4777 return FALSE;
4779 /* convert old style (MIIM_TYPE) to the new */
4780 if( pmii_out->fMask & MIIM_TYPE){
4781 pmii_out->fMask |= MIIM_FTYPE;
4782 if( IS_STRING_ITEM(pmii_out->fType)){
4783 pmii_out->fMask |= MIIM_STRING;
4784 } else if( (pmii_out->fType) & MFT_BITMAP){
4785 pmii_out->fMask |= MIIM_BITMAP;
4786 pmii_out->hbmpItem = HBITMAP_32(LOWORD(pmii_out->dwTypeData));
4789 return TRUE;
4792 /**********************************************************************
4793 * SetMenuItemInfoA (USER32.@)
4795 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4796 const MENUITEMINFOA *lpmii)
4798 MENUITEMINFOW mii;
4800 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4802 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4804 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4805 &mii, FALSE);
4808 /**********************************************************************
4809 * SetMenuItemInfoW (USER32.@)
4811 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4812 const MENUITEMINFOW *lpmii)
4814 MENUITEMINFOW mii;
4816 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4818 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4819 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4820 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4823 /**********************************************************************
4824 * SetMenuDefaultItem (USER32.@)
4827 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4829 UINT i;
4830 POPUPMENU *menu;
4831 MENUITEM *item;
4833 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4835 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4837 /* reset all default-item flags */
4838 item = menu->items;
4839 for (i = 0; i < menu->nItems; i++, item++)
4841 item->fState &= ~MFS_DEFAULT;
4844 /* no default item */
4845 if ( -1 == uItem)
4847 return TRUE;
4850 item = menu->items;
4851 if ( bypos )
4853 if ( uItem >= menu->nItems ) return FALSE;
4854 item[uItem].fState |= MFS_DEFAULT;
4855 return TRUE;
4857 else
4859 for (i = 0; i < menu->nItems; i++, item++)
4861 if (item->wID == uItem)
4863 item->fState |= MFS_DEFAULT;
4864 return TRUE;
4869 return FALSE;
4872 /**********************************************************************
4873 * GetMenuDefaultItem (USER32.@)
4875 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4877 POPUPMENU *menu;
4878 MENUITEM * item;
4879 UINT i = 0;
4881 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4883 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4885 /* find default item */
4886 item = menu->items;
4888 /* empty menu */
4889 if (! item) return -1;
4891 while ( !( item->fState & MFS_DEFAULT ) )
4893 i++; item++;
4894 if (i >= menu->nItems ) return -1;
4897 /* default: don't return disabled items */
4898 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4900 /* search rekursiv when needed */
4901 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4903 UINT ret;
4904 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4905 if ( -1 != ret ) return ret;
4907 /* when item not found in submenu, return the popup item */
4909 return ( bypos ) ? i : item->wID;
4914 /**********************************************************************
4915 * InsertMenuItemA (USER32.@)
4917 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4918 const MENUITEMINFOA *lpmii)
4920 MENUITEM *item;
4921 MENUITEMINFOW mii;
4923 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4925 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4927 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4928 return SetMenuItemInfo_common(item, &mii, FALSE);
4932 /**********************************************************************
4933 * InsertMenuItemW (USER32.@)
4935 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4936 const MENUITEMINFOW *lpmii)
4938 MENUITEM *item;
4939 MENUITEMINFOW mii;
4941 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4943 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4945 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4946 return SetMenuItemInfo_common(item, &mii, TRUE);
4949 /**********************************************************************
4950 * CheckMenuRadioItem (USER32.@)
4953 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4954 UINT first, UINT last, UINT check,
4955 UINT bypos)
4957 BOOL done = FALSE;
4958 UINT i;
4959 MENUITEM *mi_first = NULL, *mi_check;
4960 HMENU m_first, m_check;
4962 for (i = first; i <= last; i++)
4964 UINT pos = i;
4966 if (!mi_first)
4968 m_first = hMenu;
4969 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4970 if (!mi_first) continue;
4971 mi_check = mi_first;
4972 m_check = m_first;
4974 else
4976 m_check = hMenu;
4977 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4978 if (!mi_check) continue;
4981 if (m_first != m_check) continue;
4982 if (mi_check->fType == MFT_SEPARATOR) continue;
4984 if (i == check)
4986 mi_check->fType |= MFT_RADIOCHECK;
4987 mi_check->fState |= MFS_CHECKED;
4988 done = TRUE;
4990 else
4992 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4993 mi_check->fState &= ~MFS_CHECKED;
4997 return done;
5001 /**********************************************************************
5002 * GetMenuItemRect (USER32.@)
5004 * ATTENTION: Here, the returned values in rect are the screen
5005 * coordinates of the item just like if the menu was
5006 * always on the upper left side of the application.
5009 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5010 LPRECT rect)
5012 POPUPMENU *itemMenu;
5013 MENUITEM *item;
5014 HWND referenceHwnd;
5016 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5018 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5019 referenceHwnd = hwnd;
5021 if(!hwnd)
5023 itemMenu = MENU_GetMenu(hMenu);
5024 if (itemMenu == NULL)
5025 return FALSE;
5027 if(itemMenu->hWnd == 0)
5028 return FALSE;
5029 referenceHwnd = itemMenu->hWnd;
5032 if ((rect == NULL) || (item == NULL))
5033 return FALSE;
5035 *rect = item->rect;
5037 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5039 return TRUE;
5042 /**********************************************************************
5043 * SetMenuInfo (USER32.@)
5045 * FIXME
5046 * actually use the items to draw the menu
5047 * (recalculate and/or redraw)
5049 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5051 POPUPMENU *menu;
5052 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5054 if (lpmi->fMask & MIM_BACKGROUND)
5055 menu->hbrBack = lpmi->hbrBack;
5057 if (lpmi->fMask & MIM_HELPID)
5058 menu->dwContextHelpID = lpmi->dwContextHelpID;
5060 if (lpmi->fMask & MIM_MAXHEIGHT)
5061 menu->cyMax = lpmi->cyMax;
5063 if (lpmi->fMask & MIM_MENUDATA)
5064 menu->dwMenuData = lpmi->dwMenuData;
5066 if (lpmi->fMask & MIM_STYLE)
5067 menu->dwStyle = lpmi->dwStyle;
5069 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5070 int i;
5071 MENUITEM *item = menu->items;
5072 for( i = menu->nItems; i; i--, item++)
5073 if( item->fType & MF_POPUP)
5074 menu_SetMenuInfo( item->hSubMenu, lpmi);
5076 return TRUE;
5079 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5081 TRACE("(%p %p)\n", hMenu, lpmi);
5082 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5083 if( lpmi->fMask & MIM_STYLE) {
5084 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5085 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5086 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5088 return TRUE;
5090 SetLastError( ERROR_INVALID_PARAMETER);
5091 return FALSE;
5094 /**********************************************************************
5095 * GetMenuInfo (USER32.@)
5097 * NOTES
5098 * win98/NT5.0
5101 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5102 { POPUPMENU *menu;
5104 TRACE("(%p %p)\n", hMenu, lpmi);
5106 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5109 if (lpmi->fMask & MIM_BACKGROUND)
5110 lpmi->hbrBack = menu->hbrBack;
5112 if (lpmi->fMask & MIM_HELPID)
5113 lpmi->dwContextHelpID = menu->dwContextHelpID;
5115 if (lpmi->fMask & MIM_MAXHEIGHT)
5116 lpmi->cyMax = menu->cyMax;
5118 if (lpmi->fMask & MIM_MENUDATA)
5119 lpmi->dwMenuData = menu->dwMenuData;
5121 if (lpmi->fMask & MIM_STYLE)
5122 lpmi->dwStyle = menu->dwStyle;
5124 return TRUE;
5126 SetLastError( ERROR_INVALID_PARAMETER);
5127 return FALSE;
5131 /**********************************************************************
5132 * SetMenuContextHelpId (USER32.@)
5134 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5136 LPPOPUPMENU menu;
5138 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5140 if ((menu = MENU_GetMenu(hMenu)))
5142 menu->dwContextHelpID = dwContextHelpID;
5143 return TRUE;
5145 return FALSE;
5149 /**********************************************************************
5150 * GetMenuContextHelpId (USER32.@)
5152 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5154 LPPOPUPMENU menu;
5156 TRACE("(%p)\n", hMenu);
5158 if ((menu = MENU_GetMenu(hMenu)))
5160 return menu->dwContextHelpID;
5162 return 0;
5165 /**********************************************************************
5166 * MenuItemFromPoint (USER32.@)
5168 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5170 POPUPMENU *menu = MENU_GetMenu(hMenu);
5171 UINT pos;
5173 /*FIXME: Do we have to handle hWnd here? */
5174 if (!menu) return -1;
5175 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5176 return pos;
5180 /**********************************************************************
5181 * translate_accelerator
5183 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5184 BYTE fVirt, WORD key, WORD cmd )
5186 INT mask = 0;
5187 UINT mesg = 0;
5189 if (wParam != key) return FALSE;
5191 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5192 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5193 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5195 if (message == WM_CHAR || message == WM_SYSCHAR)
5197 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5199 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5200 goto found;
5203 else
5205 if(fVirt & FVIRTKEY)
5207 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5208 wParam, 0xff & HIWORD(lParam));
5210 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5211 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5213 else
5215 if (!(lParam & 0x01000000)) /* no special_key */
5217 if ((fVirt & FALT) && (lParam & 0x20000000))
5218 { /* ^^ ALT pressed */
5219 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5220 goto found;
5225 return FALSE;
5227 found:
5228 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5229 mesg = 1;
5230 else
5232 HMENU hMenu, hSubMenu, hSysMenu;
5233 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5235 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5236 hSysMenu = get_win_sys_menu( hWnd );
5238 /* find menu item and ask application to initialize it */
5239 /* 1. in the system menu */
5240 hSubMenu = hSysMenu;
5241 nPos = cmd;
5242 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5244 if (GetCapture())
5245 mesg = 2;
5246 if (!IsWindowEnabled(hWnd))
5247 mesg = 3;
5248 else
5250 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5251 if(hSubMenu != hSysMenu)
5253 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5254 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5255 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5257 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5260 else /* 2. in the window's menu */
5262 hSubMenu = hMenu;
5263 nPos = cmd;
5264 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5266 if (GetCapture())
5267 mesg = 2;
5268 if (!IsWindowEnabled(hWnd))
5269 mesg = 3;
5270 else
5272 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5273 if(hSubMenu != hMenu)
5275 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5276 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5277 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5279 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5284 if (mesg == 0)
5286 if (uSysStat != (UINT)-1)
5288 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5289 mesg=4;
5290 else
5291 mesg=WM_SYSCOMMAND;
5293 else
5295 if (uStat != (UINT)-1)
5297 if (IsIconic(hWnd))
5298 mesg=5;
5299 else
5301 if (uStat & (MF_DISABLED|MF_GRAYED))
5302 mesg=6;
5303 else
5304 mesg=WM_COMMAND;
5307 else
5308 mesg=WM_COMMAND;
5313 if( mesg==WM_COMMAND )
5315 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5316 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5318 else if( mesg==WM_SYSCOMMAND )
5320 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5321 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5323 else
5325 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5326 * #0: unknown (please report!)
5327 * #1: for WM_KEYUP,WM_SYSKEYUP
5328 * #2: mouse is captured
5329 * #3: window is disabled
5330 * #4: it's a disabled system menu option
5331 * #5: it's a menu option, but window is iconic
5332 * #6: it's a menu option, but disabled
5334 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5335 if(mesg==0)
5336 ERR_(accel)(" unknown reason - please report!\n");
5338 return TRUE;
5341 /**********************************************************************
5342 * TranslateAcceleratorA (USER32.@)
5343 * TranslateAccelerator (USER32.@)
5345 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5347 /* YES, Accel16! */
5348 LPACCEL16 lpAccelTbl;
5349 int i;
5350 WPARAM wParam;
5352 if (!hWnd || !msg) return 0;
5354 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5356 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5357 return 0;
5360 wParam = msg->wParam;
5362 switch (msg->message)
5364 case WM_KEYDOWN:
5365 case WM_SYSKEYDOWN:
5366 break;
5368 case WM_CHAR:
5369 case WM_SYSCHAR:
5371 char ch = LOWORD(wParam);
5372 WCHAR wch;
5373 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5374 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5376 break;
5378 default:
5379 return 0;
5382 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5383 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5384 i = 0;
5387 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5388 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5389 return 1;
5390 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5392 return 0;
5395 /**********************************************************************
5396 * TranslateAcceleratorW (USER32.@)
5398 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5400 /* YES, Accel16! */
5401 LPACCEL16 lpAccelTbl;
5402 int i;
5404 if (!hWnd || !msg) return 0;
5406 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5408 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5409 return 0;
5412 switch (msg->message)
5414 case WM_KEYDOWN:
5415 case WM_SYSKEYDOWN:
5416 case WM_CHAR:
5417 case WM_SYSCHAR:
5418 break;
5420 default:
5421 return 0;
5424 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5425 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5426 i = 0;
5429 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5430 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5431 return 1;
5432 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5434 return 0;