push 6377a023768d7480eb1b17e57456824888936b43
[wine/hacks.git] / dlls / user32 / menu.c
blob203340a3bef4b4a9b2c45bb14195402a7920c8c8
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 SIZE maxBmpSize; /* Maximum size of the bitmap items */
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->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1075 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1076 lpitem->rect.right += size.cx + 2;
1077 itemheight = size.cy + 2;
1079 if( !(lppop->dwStyle & MNS_NOCHECK))
1080 lpitem->rect.right += check_bitmap_width;
1081 lpitem->rect.right += 4 + menucharsize.cx;
1082 lpitem->xTab = lpitem->rect.right;
1083 lpitem->rect.right += arrow_bitmap_width;
1084 } else if (lpitem->hbmpItem) { /* menuBar */
1085 SIZE size;
1087 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1088 lpitem->bmpsize = size;
1089 lpitem->rect.right += size.cx;
1090 if( lpitem->text) lpitem->rect.right += 2;
1091 itemheight = size.cy;
1094 /* it must be a text item - unless it's the system menu */
1095 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1096 HFONT hfontOld = NULL;
1097 RECT rc = lpitem->rect;
1098 LONG txtheight, txtwidth;
1100 if ( lpitem->fState & MFS_DEFAULT ) {
1101 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1103 if (menuBar) {
1104 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1105 DT_SINGLELINE|DT_CALCRECT);
1106 lpitem->rect.right += rc.right - rc.left;
1107 itemheight = max( max( itemheight, txtheight),
1108 GetSystemMetrics( SM_CYMENU) - 1);
1109 lpitem->rect.right += 2 * menucharsize.cx;
1110 } else {
1111 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1112 RECT tmprc = rc;
1113 LONG tmpheight;
1114 int n = (int)( p - lpitem->text);
1115 /* Item contains a tab (only meaningful in popup menus) */
1116 /* get text size before the tab */
1117 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1118 DT_SINGLELINE|DT_CALCRECT);
1119 txtwidth = rc.right - rc.left;
1120 p += 1; /* advance past the Tab */
1121 /* get text size after the tab */
1122 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1123 DT_SINGLELINE|DT_CALCRECT);
1124 lpitem->xTab += txtwidth;
1125 txtheight = max( txtheight, tmpheight);
1126 txtwidth += menucharsize.cx + /* space for the tab */
1127 tmprc.right - tmprc.left; /* space for the short cut */
1128 } else {
1129 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1130 DT_SINGLELINE|DT_CALCRECT);
1131 txtwidth = rc.right - rc.left;
1132 lpitem->xTab += txtwidth;
1134 lpitem->rect.right += 2 + txtwidth;
1135 itemheight = max( itemheight,
1136 max( txtheight + 2, menucharsize.cy + 4));
1138 if (hfontOld) SelectObject (hdc, hfontOld);
1139 } else if( menuBar) {
1140 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1142 lpitem->rect.bottom += itemheight;
1143 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1147 /***********************************************************************
1148 * MENU_GetMaxPopupHeight
1150 static UINT
1151 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1153 if (lppop->cyMax)
1154 return lppop->cyMax;
1155 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1159 /***********************************************************************
1160 * MENU_PopupMenuCalcSize
1162 * Calculate the size of a popup menu.
1164 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1166 MENUITEM *lpitem;
1167 HDC hdc;
1168 UINT start, i;
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->maxBmpSize.cx = 0;
1181 lppop->maxBmpSize.cy = 0;
1183 while (start < lppop->nItems)
1185 lpitem = &lppop->items[start];
1186 orgX = maxX;
1187 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1188 orgX += MENU_COL_SPACE;
1189 orgY = MENU_TOP_MARGIN;
1191 maxTab = maxTabWidth = 0;
1192 /* Parse items until column break or end of menu */
1193 for (i = start; i < lppop->nItems; i++, lpitem++)
1195 if ((i != start) &&
1196 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1198 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1199 maxX = max( maxX, lpitem->rect.right );
1200 orgY = lpitem->rect.bottom;
1201 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1203 maxTab = max( maxTab, lpitem->xTab );
1204 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
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;
1222 /* space for 3d border */
1223 lppop->Height += MENU_BOTTOM_MARGIN;
1224 lppop->Width += 2;
1226 /* Adjust popup height if it exceeds maximum */
1227 maxHeight = MENU_GetMaxPopupHeight(lppop);
1228 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1229 if (lppop->Height >= maxHeight)
1231 lppop->Height = maxHeight;
1232 lppop->bScrolling = TRUE;
1234 else
1236 lppop->bScrolling = FALSE;
1239 ReleaseDC( 0, hdc );
1243 /***********************************************************************
1244 * MENU_MenuBarCalcSize
1246 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1247 * height is off by 1 pixel which causes lengthy window relocations when
1248 * active document window is maximized/restored.
1250 * Calculate the size of the menu bar.
1252 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1253 LPPOPUPMENU lppop, HWND hwndOwner )
1255 MENUITEM *lpitem;
1256 UINT start, i, helpPos;
1257 int orgX, orgY, maxY;
1259 if ((lprect == NULL) || (lppop == NULL)) return;
1260 if (lppop->nItems == 0) return;
1261 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1262 lppop->Width = lprect->right - lprect->left;
1263 lppop->Height = 0;
1264 maxY = lprect->top+1;
1265 start = 0;
1266 helpPos = ~0U;
1267 lppop->maxBmpSize.cx = 0;
1268 lppop->maxBmpSize.cy = 0;
1269 while (start < lppop->nItems)
1271 lpitem = &lppop->items[start];
1272 orgX = lprect->left;
1273 orgY = maxY;
1275 /* Parse items until line break or end of menu */
1276 for (i = start; i < lppop->nItems; i++, lpitem++)
1278 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1279 if ((i != start) &&
1280 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1282 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1283 debug_print_menuitem (" item: ", lpitem, "");
1284 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1286 if (lpitem->rect.right > lprect->right)
1288 if (i != start) break;
1289 else lpitem->rect.right = lprect->right;
1291 maxY = max( maxY, lpitem->rect.bottom );
1292 orgX = lpitem->rect.right;
1295 /* Finish the line (set all items to the largest height found) */
1296 while (start < i) lppop->items[start++].rect.bottom = maxY;
1299 lprect->bottom = maxY;
1300 lppop->Height = lprect->bottom - lprect->top;
1302 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1303 /* the last item (if several lines, only move the last line) */
1304 if (helpPos == ~0U) return;
1305 lpitem = &lppop->items[lppop->nItems-1];
1306 orgY = lpitem->rect.top;
1307 orgX = lprect->right;
1308 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1309 if (lpitem->rect.top != orgY) break; /* Other line */
1310 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1311 lpitem->rect.left += orgX - lpitem->rect.right;
1312 lpitem->rect.right = orgX;
1313 orgX = lpitem->rect.left;
1318 /***********************************************************************
1319 * MENU_DrawScrollArrows
1321 * Draw scroll arrows.
1323 static void
1324 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1326 HDC hdcMem = CreateCompatibleDC(hdc);
1327 HBITMAP hOrigBitmap;
1328 UINT arrow_bitmap_width, arrow_bitmap_height;
1329 BITMAP bmp;
1330 RECT rect;
1332 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1333 arrow_bitmap_width = bmp.bmWidth;
1334 arrow_bitmap_height = bmp.bmHeight;
1337 if (lppop->nScrollPos)
1338 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1339 else
1340 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1341 rect.left = 0;
1342 rect.top = 0;
1343 rect.right = lppop->Width;
1344 rect.bottom = arrow_bitmap_height;
1345 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1346 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1347 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1348 rect.top = lppop->Height - arrow_bitmap_height;
1349 rect.bottom = lppop->Height;
1350 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1351 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1352 SelectObject(hdcMem, get_down_arrow_bitmap());
1353 else
1354 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1355 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1356 lppop->Height - arrow_bitmap_height,
1357 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1358 SelectObject(hdcMem, hOrigBitmap);
1359 DeleteDC(hdcMem);
1363 /***********************************************************************
1364 * draw_popup_arrow
1366 * Draws the popup-menu arrow.
1368 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1369 UINT arrow_bitmap_height)
1371 HDC hdcMem = CreateCompatibleDC( hdc );
1372 HBITMAP hOrigBitmap;
1374 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1375 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1376 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1377 arrow_bitmap_width, arrow_bitmap_height,
1378 hdcMem, 0, 0, SRCCOPY );
1379 SelectObject( hdcMem, hOrigBitmap );
1380 DeleteDC( hdcMem );
1382 /***********************************************************************
1383 * MENU_DrawMenuItem
1385 * Draw a single menu item.
1387 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1388 UINT height, BOOL menuBar, UINT odaction )
1390 RECT rect;
1391 BOOL flat_menu = FALSE;
1392 int bkgnd;
1393 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1394 POPUPMENU *menu = MENU_GetMenu(hmenu);
1395 RECT bmprc;
1397 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1399 if (!menuBar) {
1400 BITMAP bmp;
1401 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1402 arrow_bitmap_width = bmp.bmWidth;
1403 arrow_bitmap_height = bmp.bmHeight;
1406 if (lpitem->fType & MF_SYSMENU)
1408 if( !IsIconic(hwnd) )
1409 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1410 return;
1413 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1414 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1416 /* Setup colors */
1418 if (lpitem->fState & MF_HILITE)
1420 if(menuBar && !flat_menu) {
1421 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1422 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1423 } else {
1424 if(lpitem->fState & MF_GRAYED)
1425 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1426 else
1427 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1428 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1431 else
1433 if (lpitem->fState & MF_GRAYED)
1434 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1435 else
1436 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1437 SetBkColor( hdc, GetSysColor( bkgnd ) );
1440 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1441 rect = lpitem->rect;
1442 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1444 if (lpitem->fType & MF_OWNERDRAW)
1447 ** Experimentation under Windows reveals that an owner-drawn
1448 ** menu is given the rectangle which includes the space it requested
1449 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1450 ** and a popup-menu arrow. This is the value of lpitem->rect.
1451 ** Windows will leave all drawing to the application except for
1452 ** the popup-menu arrow. Windows always draws that itself, after
1453 ** the menu owner has finished drawing.
1455 DRAWITEMSTRUCT dis;
1457 dis.CtlType = ODT_MENU;
1458 dis.CtlID = 0;
1459 dis.itemID = lpitem->wID;
1460 dis.itemData = lpitem->dwItemData;
1461 dis.itemState = 0;
1462 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1463 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1464 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1465 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1466 dis.hwndItem = (HWND)hmenu;
1467 dis.hDC = hdc;
1468 dis.rcItem = rect;
1469 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1470 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1471 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1472 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1473 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1474 /* Draw the popup-menu arrow */
1475 if (lpitem->fType & MF_POPUP)
1476 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1477 arrow_bitmap_height);
1478 return;
1481 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1483 if (lpitem->fState & MF_HILITE)
1485 if (flat_menu)
1487 InflateRect (&rect, -1, -1);
1488 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1489 InflateRect (&rect, 1, 1);
1490 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1492 else
1494 if(menuBar)
1495 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1496 else
1497 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1500 else
1501 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1503 SetBkMode( hdc, TRANSPARENT );
1505 /* vertical separator */
1506 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1508 HPEN oldPen;
1509 RECT rc = rect;
1511 rc.left -= MENU_COL_SPACE / 2 + 1;
1512 rc.top = 3;
1513 rc.bottom = height - 3;
1514 if (flat_menu)
1516 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1517 MoveToEx( hdc, rc.left, rc.top, NULL );
1518 LineTo( hdc, rc.left, rc.bottom );
1519 SelectObject( hdc, oldPen );
1521 else
1522 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1525 /* horizontal separator */
1526 if (lpitem->fType & MF_SEPARATOR)
1528 HPEN oldPen;
1529 RECT rc = rect;
1531 rc.left++;
1532 rc.right--;
1533 rc.top = ( rc.top + rc.bottom) / 2;
1534 if (flat_menu)
1536 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1537 MoveToEx( hdc, rc.left, rc.top, NULL );
1538 LineTo( hdc, rc.right, rc.top );
1539 SelectObject( hdc, oldPen );
1541 else
1542 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1543 return;
1546 /* helper lines for debugging */
1547 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1548 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1549 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1550 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1553 if (lpitem->hbmpItem) {
1554 /* calculate the bitmap rectangle in coordinates relative
1555 * to the item rectangle */
1556 if( menuBar) {
1557 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1558 bmprc.left = 3;
1559 else
1560 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1562 else if (menu->dwStyle & MNS_NOCHECK)
1563 bmprc.left = 4;
1564 else if (menu->dwStyle & MNS_CHECKORBMP)
1565 bmprc.left = 2;
1566 else
1567 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1568 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1569 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1570 bmprc.top = 0;
1571 else
1572 bmprc.top = (rect.bottom - rect.top -
1573 lpitem->bmpsize.cy) / 2;
1574 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1577 if (!menuBar)
1579 HBITMAP bm;
1580 INT y = rect.top + rect.bottom;
1581 RECT rc = rect;
1582 int checked = FALSE;
1583 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1584 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1585 /* Draw the check mark
1587 * FIXME:
1588 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1590 if( !(menu->dwStyle & MNS_NOCHECK)) {
1591 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1592 lpitem->hUnCheckBit;
1593 if (bm) /* we have a custom bitmap */
1595 HDC hdcMem = CreateCompatibleDC( hdc );
1597 SelectObject( hdcMem, bm );
1598 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1599 check_bitmap_width, check_bitmap_height,
1600 hdcMem, 0, 0, SRCCOPY );
1601 DeleteDC( hdcMem );
1602 checked = TRUE;
1604 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1606 RECT r;
1607 HBITMAP bm = CreateBitmap( check_bitmap_width,
1608 check_bitmap_height, 1, 1, NULL );
1609 HDC hdcMem = CreateCompatibleDC( hdc );
1611 SelectObject( hdcMem, bm );
1612 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1613 DrawFrameControl( hdcMem, &r, DFC_MENU,
1614 (lpitem->fType & MFT_RADIOCHECK) ?
1615 DFCS_MENUBULLET : DFCS_MENUCHECK );
1616 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1617 hdcMem, 0, 0, SRCCOPY );
1618 DeleteDC( hdcMem );
1619 DeleteObject( bm );
1620 checked = TRUE;
1623 if( lpitem->hbmpItem &&
1624 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1625 POINT origorg;
1626 /* some applications make this assumption on the DC's origin */
1627 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1628 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1629 odaction, FALSE);
1630 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1632 /* Draw the popup-menu arrow */
1633 if (lpitem->fType & MF_POPUP)
1634 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1635 arrow_bitmap_height);
1636 rect.left += 4;
1637 if( !(menu->dwStyle & MNS_NOCHECK))
1638 rect.left += check_bitmap_width;
1639 rect.right -= arrow_bitmap_width;
1641 else if( lpitem->hbmpItem)
1642 { /* Draw the bitmap */
1643 POINT origorg;
1645 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1646 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1647 odaction, menuBar);
1648 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1650 /* process text if present */
1651 if (lpitem->text)
1653 register int i;
1654 HFONT hfontOld = 0;
1656 UINT uFormat = (menuBar) ?
1657 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1658 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1660 if( !(menu->dwStyle & MNS_CHECKORBMP))
1661 rect.left += menu->maxBmpSize.cx;
1663 if ( lpitem->fState & MFS_DEFAULT )
1665 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1668 if (menuBar) {
1669 if( lpitem->hbmpItem)
1670 rect.left += lpitem->bmpsize.cx;
1671 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1672 rect.left += menucharsize.cx;
1673 rect.right -= menucharsize.cx;
1676 for (i = 0; lpitem->text[i]; i++)
1677 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1678 break;
1680 if(lpitem->fState & MF_GRAYED)
1682 if (!(lpitem->fState & MF_HILITE) )
1684 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1685 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1686 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1687 --rect.left; --rect.top; --rect.right; --rect.bottom;
1689 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1692 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1694 /* paint the shortcut text */
1695 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1697 if (lpitem->text[i] == '\t')
1699 rect.left = lpitem->xTab;
1700 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1702 else
1704 rect.right = lpitem->xTab;
1705 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1708 if(lpitem->fState & MF_GRAYED)
1710 if (!(lpitem->fState & MF_HILITE) )
1712 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1713 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1714 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1715 --rect.left; --rect.top; --rect.right; --rect.bottom;
1717 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1719 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1722 if (hfontOld)
1723 SelectObject (hdc, hfontOld);
1728 /***********************************************************************
1729 * MENU_DrawPopupMenu
1731 * Paint a popup menu.
1733 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1735 HBRUSH hPrevBrush = 0;
1736 RECT rect;
1738 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1740 GetClientRect( hwnd, &rect );
1742 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1743 && (SelectObject( hdc, get_menu_font(FALSE))))
1745 HPEN hPrevPen;
1747 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1749 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1750 if( hPrevPen )
1752 POPUPMENU *menu;
1753 BOOL flat_menu = FALSE;
1755 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1756 if (flat_menu)
1757 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1758 else
1759 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1761 if( (menu = MENU_GetMenu( hmenu )))
1763 /* draw menu items */
1764 if( menu->nItems)
1766 MENUITEM *item;
1767 UINT u;
1769 item = menu->items;
1770 for( u = menu->nItems; u > 0; u--, item++)
1771 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1772 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1774 /* draw scroll arrows */
1775 if (menu->bScrolling)
1776 MENU_DrawScrollArrows(menu, hdc);
1778 } else
1780 SelectObject( hdc, hPrevBrush );
1785 /***********************************************************************
1786 * MENU_DrawMenuBar
1788 * Paint a menu bar. Returns the height of the menu bar.
1789 * called from [windows/nonclient.c]
1791 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1792 BOOL suppress_draw)
1794 LPPOPUPMENU lppop;
1795 HFONT hfontOld = 0;
1796 HMENU hMenu = GetMenu(hwnd);
1798 lppop = MENU_GetMenu( hMenu );
1799 if (lppop == NULL || lprect == NULL)
1801 return GetSystemMetrics(SM_CYMENU);
1804 if (suppress_draw)
1806 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1808 if (lppop->Height == 0)
1809 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1811 lprect->bottom = lprect->top + lppop->Height;
1813 if (hfontOld) SelectObject( hDC, hfontOld);
1814 return lppop->Height;
1816 else
1817 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1821 /***********************************************************************
1822 * MENU_ShowPopup
1824 * Display a popup menu.
1826 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1827 INT x, INT y, INT xanchor, INT yanchor )
1829 POPUPMENU *menu;
1830 INT width, height;
1831 POINT pt;
1832 HMONITOR monitor;
1833 MONITORINFO info;
1835 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1836 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1838 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1839 if (menu->FocusedItem != NO_SELECTED_ITEM)
1841 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1842 menu->FocusedItem = NO_SELECTED_ITEM;
1845 /* store the owner for DrawItem */
1846 menu->hwndOwner = hwndOwner;
1848 menu->nScrollPos = 0;
1849 MENU_PopupMenuCalcSize( menu );
1851 /* adjust popup menu pos so that it fits within the desktop */
1853 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1854 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1856 /* FIXME: should use item rect */
1857 pt.x = x;
1858 pt.y = y;
1859 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1860 info.cbSize = sizeof(info);
1861 GetMonitorInfoW( monitor, &info );
1863 if( flags & TPM_RIGHTALIGN ) x -= width;
1864 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1866 if( flags & TPM_BOTTOMALIGN ) y -= height;
1867 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1869 if( x + width > info.rcWork.right)
1871 if( xanchor && x >= width - xanchor )
1872 x -= width - xanchor;
1874 if( x + width > info.rcWork.right)
1875 x = info.rcWork.right - width;
1877 if( x < info.rcWork.left ) x = info.rcWork.left;
1879 if( y + height > info.rcWork.bottom)
1881 if( yanchor && y >= height + yanchor )
1882 y -= height + yanchor;
1884 if( y + height > info.rcWork.bottom)
1885 y = info.rcWork.bottom - height;
1887 if( y < info.rcWork.top ) y = info.rcWork.top;
1889 /* NOTE: In Windows, top menu popup is not owned. */
1890 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1891 WS_POPUP, x, y, width, height,
1892 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1893 (LPVOID)hmenu );
1894 if( !menu->hWnd ) return FALSE;
1895 if (!top_popup) {
1896 top_popup = menu->hWnd;
1897 top_popup_hmenu = hmenu;
1899 /* Display the window */
1901 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1902 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1903 UpdateWindow( menu->hWnd );
1904 return TRUE;
1908 /***********************************************************************
1909 * MENU_EnsureMenuItemVisible
1911 static void
1912 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1914 if (lppop->bScrolling)
1916 MENUITEM *item = &lppop->items[wIndex];
1917 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1918 UINT nOldPos = lppop->nScrollPos;
1919 RECT rc;
1920 UINT arrow_bitmap_height;
1921 BITMAP bmp;
1923 GetClientRect(lppop->hWnd, &rc);
1925 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1926 arrow_bitmap_height = bmp.bmHeight;
1928 rc.top += arrow_bitmap_height;
1929 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1931 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1932 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1935 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1936 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1937 MENU_DrawScrollArrows(lppop, hdc);
1939 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1941 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1942 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1943 MENU_DrawScrollArrows(lppop, hdc);
1949 /***********************************************************************
1950 * MENU_SelectItem
1952 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1953 BOOL sendMenuSelect, HMENU topmenu )
1955 LPPOPUPMENU lppop;
1956 HDC hdc;
1958 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1960 lppop = MENU_GetMenu( hmenu );
1961 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1963 if (lppop->FocusedItem == wIndex) return;
1964 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1965 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1966 if (!top_popup) {
1967 top_popup = lppop->hWnd;
1968 top_popup_hmenu = hmenu;
1971 SelectObject( hdc, get_menu_font(FALSE));
1973 /* Clear previous highlighted item */
1974 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1976 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1977 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1978 lppop->Height, !(lppop->wFlags & MF_POPUP),
1979 ODA_SELECT );
1982 /* Highlight new item (if any) */
1983 lppop->FocusedItem = wIndex;
1984 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1986 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1987 lppop->items[wIndex].fState |= MF_HILITE;
1988 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1989 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1990 &lppop->items[wIndex], lppop->Height,
1991 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1993 if (sendMenuSelect)
1995 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1996 SendMessageW( hwndOwner, WM_MENUSELECT,
1997 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
1998 ip->fType | ip->fState |
1999 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2002 else if (sendMenuSelect) {
2003 if(topmenu){
2004 int pos;
2005 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2006 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2007 MENUITEM *ip = &ptm->items[pos];
2008 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2009 ip->fType | ip->fState |
2010 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2014 ReleaseDC( lppop->hWnd, hdc );
2018 /***********************************************************************
2019 * MENU_MoveSelection
2021 * Moves currently selected item according to the offset parameter.
2022 * If there is no selection then it should select the last item if
2023 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2025 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2027 INT i;
2028 POPUPMENU *menu;
2030 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2032 menu = MENU_GetMenu( hmenu );
2033 if ((!menu) || (!menu->items)) return;
2035 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2037 if( menu->nItems == 1 ) return; else
2038 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2039 ; i += offset)
2040 if (!(menu->items[i].fType & MF_SEPARATOR))
2042 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2043 return;
2047 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2048 i >= 0 && i < menu->nItems ; i += offset)
2049 if (!(menu->items[i].fType & MF_SEPARATOR))
2051 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2052 return;
2057 /**********************************************************************
2058 * MENU_InsertItem
2060 * Insert (allocate) a new item into a menu.
2062 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2064 MENUITEM *newItems;
2065 POPUPMENU *menu;
2067 if (!(menu = MENU_GetMenu(hMenu)))
2068 return NULL;
2070 /* Find where to insert new item */
2072 if (flags & MF_BYPOSITION) {
2073 if (pos > menu->nItems)
2074 pos = menu->nItems;
2075 } else {
2076 if (!MENU_FindItem( &hMenu, &pos, flags ))
2077 pos = menu->nItems;
2078 else {
2079 if (!(menu = MENU_GetMenu( hMenu )))
2080 return NULL;
2084 /* Make sure that MDI system buttons stay on the right side.
2085 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2086 * regardless of their id.
2088 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2089 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2090 pos--;
2092 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2094 /* Create new items array */
2096 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2097 if (!newItems)
2099 WARN("allocation failed\n" );
2100 return NULL;
2102 if (menu->nItems > 0)
2104 /* Copy the old array into the new one */
2105 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2106 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2107 (menu->nItems-pos)*sizeof(MENUITEM) );
2108 HeapFree( GetProcessHeap(), 0, menu->items );
2110 menu->items = newItems;
2111 menu->nItems++;
2112 memset( &newItems[pos], 0, sizeof(*newItems) );
2113 menu->Height = 0; /* force size recalculate */
2114 return &newItems[pos];
2118 /**********************************************************************
2119 * MENU_ParseResource
2121 * Parse a standard menu resource and add items to the menu.
2122 * Return a pointer to the end of the resource.
2124 * NOTE: flags is equivalent to the mtOption field
2126 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2128 WORD flags, id = 0;
2129 LPCSTR str;
2130 BOOL end_flag;
2134 flags = GET_WORD(res);
2135 end_flag = flags & MF_END;
2136 /* Remove MF_END because it has the same value as MF_HILITE */
2137 flags &= ~MF_END;
2138 res += sizeof(WORD);
2139 if (!(flags & MF_POPUP))
2141 id = GET_WORD(res);
2142 res += sizeof(WORD);
2144 str = res;
2145 if (!unicode) res += strlen(str) + 1;
2146 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2147 if (flags & MF_POPUP)
2149 HMENU hSubMenu = CreatePopupMenu();
2150 if (!hSubMenu) return NULL;
2151 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2152 return NULL;
2153 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2154 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2156 else /* Not a popup */
2158 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2159 else AppendMenuW( hMenu, flags, id,
2160 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2162 } while (!end_flag);
2163 return res;
2167 /**********************************************************************
2168 * MENUEX_ParseResource
2170 * Parse an extended menu resource and add items to the menu.
2171 * Return a pointer to the end of the resource.
2173 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2175 WORD resinfo;
2176 do {
2177 MENUITEMINFOW mii;
2179 mii.cbSize = sizeof(mii);
2180 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2181 mii.fType = GET_DWORD(res);
2182 res += sizeof(DWORD);
2183 mii.fState = GET_DWORD(res);
2184 res += sizeof(DWORD);
2185 mii.wID = GET_DWORD(res);
2186 res += sizeof(DWORD);
2187 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2188 res += sizeof(WORD);
2189 /* Align the text on a word boundary. */
2190 res += (~((UINT_PTR)res - 1)) & 1;
2191 mii.dwTypeData = (LPWSTR) res;
2192 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2193 /* Align the following fields on a dword boundary. */
2194 res += (~((UINT_PTR)res - 1)) & 3;
2196 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2197 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2199 if (resinfo & 1) { /* Pop-up? */
2200 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2201 res += sizeof(DWORD);
2202 mii.hSubMenu = CreatePopupMenu();
2203 if (!mii.hSubMenu)
2204 return NULL;
2205 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2206 DestroyMenu(mii.hSubMenu);
2207 return NULL;
2209 mii.fMask |= MIIM_SUBMENU;
2210 mii.fType |= MF_POPUP;
2212 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2214 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2215 mii.wID, mii.fType);
2216 mii.fType |= MF_SEPARATOR;
2218 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2219 } while (!(resinfo & MF_END));
2220 return res;
2224 /***********************************************************************
2225 * MENU_GetSubPopup
2227 * Return the handle of the selected sub-popup menu (if any).
2229 static HMENU MENU_GetSubPopup( HMENU hmenu )
2231 POPUPMENU *menu;
2232 MENUITEM *item;
2234 menu = MENU_GetMenu( hmenu );
2236 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2238 item = &menu->items[menu->FocusedItem];
2239 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2240 return item->hSubMenu;
2241 return 0;
2245 /***********************************************************************
2246 * MENU_HideSubPopups
2248 * Hide the sub-popup menus of this menu.
2250 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2251 BOOL sendMenuSelect, UINT wFlags )
2253 POPUPMENU *menu = MENU_GetMenu( hmenu );
2255 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2257 if (menu && top_popup)
2259 HMENU hsubmenu;
2260 POPUPMENU *submenu;
2261 MENUITEM *item;
2263 if (menu->FocusedItem != NO_SELECTED_ITEM)
2265 item = &menu->items[menu->FocusedItem];
2266 if (!(item->fType & MF_POPUP) ||
2267 !(item->fState & MF_MOUSESELECT)) return;
2268 item->fState &= ~MF_MOUSESELECT;
2269 hsubmenu = item->hSubMenu;
2270 } else return;
2272 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2273 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2274 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2275 DestroyWindow( submenu->hWnd );
2276 submenu->hWnd = 0;
2278 if (!(wFlags & TPM_NONOTIFY))
2279 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2280 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2285 /***********************************************************************
2286 * MENU_ShowSubPopup
2288 * Display the sub-menu of the selected item of this menu.
2289 * Return the handle of the submenu, or hmenu if no submenu to display.
2291 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2292 BOOL selectFirst, UINT wFlags )
2294 RECT rect;
2295 POPUPMENU *menu;
2296 MENUITEM *item;
2297 HDC hdc;
2299 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2301 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2303 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2305 item = &menu->items[menu->FocusedItem];
2306 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2307 return hmenu;
2309 /* message must be sent before using item,
2310 because nearly everything may be changed by the application ! */
2312 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2313 if (!(wFlags & TPM_NONOTIFY))
2314 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2315 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2317 item = &menu->items[menu->FocusedItem];
2318 rect = item->rect;
2320 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2321 if (!(item->fState & MF_HILITE))
2323 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2324 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2326 SelectObject( hdc, get_menu_font(FALSE));
2328 item->fState |= MF_HILITE;
2329 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2330 ReleaseDC( menu->hWnd, hdc );
2332 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2333 item->rect = rect;
2335 item->fState |= MF_MOUSESELECT;
2337 if (IS_SYSTEM_MENU(menu))
2339 MENU_InitSysMenuPopup(item->hSubMenu,
2340 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2341 GetClassLongW( menu->hWnd, GCL_STYLE));
2343 NC_GetSysPopupPos( menu->hWnd, &rect );
2344 rect.top = rect.bottom;
2345 rect.right = GetSystemMetrics(SM_CXSIZE);
2346 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2348 else
2350 GetWindowRect( menu->hWnd, &rect );
2351 if (menu->wFlags & MF_POPUP)
2353 RECT rc = item->rect;
2355 MENU_AdjustMenuItemRect(menu, &rc);
2357 /* The first item in the popup menu has to be at the
2358 same y position as the focused menu item */
2359 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2360 rect.top += rc.top - MENU_TOP_MARGIN;
2361 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2362 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2363 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2365 else
2367 rect.left += item->rect.left;
2368 rect.top += item->rect.bottom;
2369 rect.right = item->rect.right - item->rect.left;
2370 rect.bottom = item->rect.bottom - item->rect.top;
2374 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2375 rect.left, rect.top, rect.right, rect.bottom );
2376 if (selectFirst)
2377 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2378 return item->hSubMenu;
2383 /**********************************************************************
2384 * MENU_IsMenuActive
2386 HWND MENU_IsMenuActive(void)
2388 return top_popup;
2391 /**********************************************************************
2392 * MENU_EndMenu
2394 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2396 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2398 void MENU_EndMenu( HWND hwnd )
2400 POPUPMENU *menu;
2401 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2402 if (menu && hwnd == menu->hwndOwner) EndMenu();
2405 /***********************************************************************
2406 * MENU_PtMenu
2408 * Walks menu chain trying to find a menu pt maps to.
2410 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2412 POPUPMENU *menu = MENU_GetMenu( hMenu );
2413 UINT item = menu->FocusedItem;
2414 HMENU ret;
2416 /* try subpopup first (if any) */
2417 ret = (item != NO_SELECTED_ITEM &&
2418 (menu->items[item].fType & MF_POPUP) &&
2419 (menu->items[item].fState & MF_MOUSESELECT))
2420 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2422 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2424 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2425 if( menu->wFlags & MF_POPUP )
2427 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2429 else if (ht == HTSYSMENU)
2430 ret = get_win_sys_menu( menu->hWnd );
2431 else if (ht == HTMENU)
2432 ret = GetMenu( menu->hWnd );
2434 return ret;
2437 /***********************************************************************
2438 * MENU_ExecFocusedItem
2440 * Execute a menu item (for instance when user pressed Enter).
2441 * Return the wID of the executed item. Otherwise, -1 indicating
2442 * that no menu item was executed, -2 if a popup is shown;
2443 * Have to receive the flags for the TrackPopupMenu options to avoid
2444 * sending unwanted message.
2447 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2449 MENUITEM *item;
2450 POPUPMENU *menu = MENU_GetMenu( hMenu );
2452 TRACE("%p hmenu=%p\n", pmt, hMenu);
2454 if (!menu || !menu->nItems ||
2455 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2457 item = &menu->items[menu->FocusedItem];
2459 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2461 if (!(item->fType & MF_POPUP))
2463 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2465 /* If TPM_RETURNCMD is set you return the id, but
2466 do not send a message to the owner */
2467 if(!(wFlags & TPM_RETURNCMD))
2469 if( menu->wFlags & MF_SYSMENU )
2470 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2471 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2472 else
2474 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2475 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2477 if (dwStyle & MNS_NOTIFYBYPOS)
2478 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2479 (LPARAM)hMenu);
2480 else
2481 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2484 return item->wID;
2487 else
2489 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2490 return -2;
2493 return -1;
2496 /***********************************************************************
2497 * MENU_SwitchTracking
2499 * Helper function for menu navigation routines.
2501 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2503 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2504 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2506 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2508 if( pmt->hTopMenu != hPtMenu &&
2509 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2511 /* both are top level menus (system and menu-bar) */
2512 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2513 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2514 pmt->hTopMenu = hPtMenu;
2516 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2517 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2521 /***********************************************************************
2522 * MENU_ButtonDown
2524 * Return TRUE if we can go on with menu tracking.
2526 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2528 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2530 if (hPtMenu)
2532 UINT id = 0;
2533 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534 MENUITEM *item;
2536 if( IS_SYSTEM_MENU(ptmenu) )
2537 item = ptmenu->items;
2538 else
2539 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2541 if( item )
2543 if( ptmenu->FocusedItem != id )
2544 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2546 /* If the popup menu is not already "popped" */
2547 if(!(item->fState & MF_MOUSESELECT ))
2549 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2552 return TRUE;
2554 /* Else the click was on the menu bar, finish the tracking */
2556 return FALSE;
2559 /***********************************************************************
2560 * MENU_ButtonUp
2562 * Return the value of MENU_ExecFocusedItem if
2563 * the selected item was not a popup. Else open the popup.
2564 * A -1 return value indicates that we go on with menu tracking.
2567 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2569 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2571 if (hPtMenu)
2573 UINT id = 0;
2574 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2575 MENUITEM *item;
2577 if( IS_SYSTEM_MENU(ptmenu) )
2578 item = ptmenu->items;
2579 else
2580 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2582 if( item && (ptmenu->FocusedItem == id ))
2584 debug_print_menuitem ("FocusedItem: ", item, "");
2586 if( !(item->fType & MF_POPUP) )
2588 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2589 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2590 return executedMenuId;
2593 /* If we are dealing with the top-level menu */
2594 /* and this is a click on an already "popped" item: */
2595 /* Stop the menu tracking and close the opened submenus */
2596 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2597 return 0;
2599 ptmenu->bTimeToHide = TRUE;
2601 return -1;
2605 /***********************************************************************
2606 * MENU_MouseMove
2608 * Return TRUE if we can go on with menu tracking.
2610 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2612 UINT id = NO_SELECTED_ITEM;
2613 POPUPMENU *ptmenu = NULL;
2615 if( hPtMenu )
2617 ptmenu = MENU_GetMenu( hPtMenu );
2618 if( IS_SYSTEM_MENU(ptmenu) )
2619 id = 0;
2620 else
2621 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2624 if( id == NO_SELECTED_ITEM )
2626 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2627 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2630 else if( ptmenu->FocusedItem != id )
2632 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2633 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2635 return TRUE;
2639 /***********************************************************************
2640 * MENU_DoNextMenu
2642 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2644 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2646 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2647 BOOL atEnd = FALSE;
2649 /* When skipping left, we need to do something special after the
2650 first menu. */
2651 if (vk == VK_LEFT && menu->FocusedItem == 0)
2653 atEnd = TRUE;
2655 /* When skipping right, for the non-system menu, we need to
2656 handle the last non-special menu item (ie skip any window
2657 icons such as MDI maximize, restore or close) */
2658 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2660 UINT i = menu->FocusedItem + 1;
2661 while (i < menu->nItems) {
2662 if ((menu->items[i].wID >= SC_SIZE &&
2663 menu->items[i].wID <= SC_RESTORE)) {
2664 i++;
2665 } else break;
2667 if (i == menu->nItems) {
2668 atEnd = TRUE;
2671 /* When skipping right, we need to cater for the system menu */
2672 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2674 if (menu->FocusedItem == (menu->nItems - 1)) {
2675 atEnd = TRUE;
2679 if( atEnd )
2681 MDINEXTMENU next_menu;
2682 HMENU hNewMenu;
2683 HWND hNewWnd;
2684 UINT id = 0;
2686 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2687 next_menu.hmenuNext = 0;
2688 next_menu.hwndNext = 0;
2689 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2691 TRACE("%p [%p] -> %p [%p]\n",
2692 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2694 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2696 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2697 hNewWnd = pmt->hOwnerWnd;
2698 if( IS_SYSTEM_MENU(menu) )
2700 /* switch to the menu bar */
2702 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2704 if( vk == VK_LEFT )
2706 menu = MENU_GetMenu( hNewMenu );
2707 id = menu->nItems - 1;
2709 /* Skip backwards over any system predefined icons,
2710 eg. MDI close, restore etc icons */
2711 while ((id > 0) &&
2712 (menu->items[id].wID >= SC_SIZE &&
2713 menu->items[id].wID <= SC_RESTORE)) id--;
2716 else if (style & WS_SYSMENU )
2718 /* switch to the system menu */
2719 hNewMenu = get_win_sys_menu( hNewWnd );
2721 else return FALSE;
2723 else /* application returned a new menu to switch to */
2725 hNewMenu = next_menu.hmenuNext;
2726 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2728 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2730 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2732 if (style & WS_SYSMENU &&
2733 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2735 /* get the real system menu */
2736 hNewMenu = get_win_sys_menu(hNewWnd);
2738 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2740 /* FIXME: Not sure what to do here;
2741 * perhaps try to track hNewMenu as a popup? */
2743 TRACE(" -- got confused.\n");
2744 return FALSE;
2747 else return FALSE;
2750 if( hNewMenu != pmt->hTopMenu )
2752 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2753 FALSE, 0 );
2754 if( pmt->hCurrentMenu != pmt->hTopMenu )
2755 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2758 if( hNewWnd != pmt->hOwnerWnd )
2760 pmt->hOwnerWnd = hNewWnd;
2761 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2764 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2765 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2767 return TRUE;
2769 return FALSE;
2772 /***********************************************************************
2773 * MENU_SuspendPopup
2775 * The idea is not to show the popup if the next input message is
2776 * going to hide it anyway.
2778 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2780 MSG msg;
2782 msg.hwnd = pmt->hOwnerWnd;
2784 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2785 pmt->trackFlags |= TF_SKIPREMOVE;
2787 switch( uMsg )
2789 case WM_KEYDOWN:
2790 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2791 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2793 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2794 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2795 if( msg.message == WM_KEYDOWN &&
2796 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2798 pmt->trackFlags |= TF_SUSPENDPOPUP;
2799 return TRUE;
2802 break;
2805 /* failures go through this */
2806 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2807 return FALSE;
2810 /***********************************************************************
2811 * MENU_KeyEscape
2813 * Handle a VK_ESCAPE key event in a menu.
2815 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2817 BOOL bEndMenu = TRUE;
2819 if (pmt->hCurrentMenu != pmt->hTopMenu)
2821 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2823 if (menu->wFlags & MF_POPUP)
2825 HMENU hmenutmp, hmenuprev;
2827 hmenuprev = hmenutmp = pmt->hTopMenu;
2829 /* close topmost popup */
2830 while (hmenutmp != pmt->hCurrentMenu)
2832 hmenuprev = hmenutmp;
2833 hmenutmp = MENU_GetSubPopup( hmenuprev );
2836 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2837 pmt->hCurrentMenu = hmenuprev;
2838 bEndMenu = FALSE;
2842 return bEndMenu;
2845 /***********************************************************************
2846 * MENU_KeyLeft
2848 * Handle a VK_LEFT key event in a menu.
2850 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2852 POPUPMENU *menu;
2853 HMENU hmenutmp, hmenuprev;
2854 UINT prevcol;
2856 hmenuprev = hmenutmp = pmt->hTopMenu;
2857 menu = MENU_GetMenu( hmenutmp );
2859 /* Try to move 1 column left (if possible) */
2860 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2861 NO_SELECTED_ITEM ) {
2863 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2864 prevcol, TRUE, 0 );
2865 return;
2868 /* close topmost popup */
2869 while (hmenutmp != pmt->hCurrentMenu)
2871 hmenuprev = hmenutmp;
2872 hmenutmp = MENU_GetSubPopup( hmenuprev );
2875 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2876 pmt->hCurrentMenu = hmenuprev;
2878 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2880 /* move menu bar selection if no more popups are left */
2882 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2883 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2885 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2887 /* A sublevel menu was displayed - display the next one
2888 * unless there is another displacement coming up */
2890 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2891 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2892 pmt->hTopMenu, TRUE, wFlags);
2898 /***********************************************************************
2899 * MENU_KeyRight
2901 * Handle a VK_RIGHT key event in a menu.
2903 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2905 HMENU hmenutmp;
2906 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2907 UINT nextcol;
2909 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2910 pmt->hCurrentMenu,
2911 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2912 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2914 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2916 /* If already displaying a popup, try to display sub-popup */
2918 hmenutmp = pmt->hCurrentMenu;
2919 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2921 /* if subpopup was displayed then we are done */
2922 if (hmenutmp != pmt->hCurrentMenu) return;
2925 /* Check to see if there's another column */
2926 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2927 NO_SELECTED_ITEM ) {
2928 TRACE("Going to %d.\n", nextcol );
2929 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2930 nextcol, TRUE, 0 );
2931 return;
2934 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2936 if( pmt->hCurrentMenu != pmt->hTopMenu )
2938 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2939 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2940 } else hmenutmp = 0;
2942 /* try to move to the next item */
2943 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2944 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2946 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2947 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2948 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2949 pmt->hTopMenu, TRUE, wFlags);
2953 static void CALLBACK release_capture( BOOL __normal )
2955 set_capture_window( 0, GUI_INMENUMODE, NULL );
2958 /***********************************************************************
2959 * MENU_TrackMenu
2961 * Menu tracking code.
2963 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2964 HWND hwnd, const RECT *lprect )
2966 MSG msg;
2967 POPUPMENU *menu;
2968 BOOL fRemove;
2969 INT executedMenuId = -1;
2970 MTRACKER mt;
2971 BOOL enterIdleSent = FALSE;
2972 HWND capture_win;
2974 mt.trackFlags = 0;
2975 mt.hCurrentMenu = hmenu;
2976 mt.hTopMenu = hmenu;
2977 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2978 mt.pt.x = x;
2979 mt.pt.y = y;
2981 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2982 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2984 fEndMenu = FALSE;
2985 if (!(menu = MENU_GetMenu( hmenu )))
2987 WARN("Invalid menu handle %p\n", hmenu);
2988 SetLastError(ERROR_INVALID_MENU_HANDLE);
2989 return FALSE;
2992 if (wFlags & TPM_BUTTONDOWN)
2994 /* Get the result in order to start the tracking or not */
2995 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2996 fEndMenu = !fRemove;
2999 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3001 /* owner may not be visible when tracking a popup, so use the menu itself */
3002 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3003 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3005 __TRY while (!fEndMenu)
3007 menu = MENU_GetMenu( mt.hCurrentMenu );
3008 if (!menu) /* sometimes happens if I do a window manager close */
3009 break;
3011 /* we have to keep the message in the queue until it's
3012 * clear that menu loop is not over yet. */
3014 for (;;)
3016 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3018 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3019 /* remove the message from the queue */
3020 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3022 else
3024 if (!enterIdleSent)
3026 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3027 enterIdleSent = TRUE;
3028 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3030 WaitMessage();
3034 /* check if EndMenu() tried to cancel us, by posting this message */
3035 if(msg.message == WM_CANCELMODE)
3037 /* we are now out of the loop */
3038 fEndMenu = TRUE;
3040 /* remove the message from the queue */
3041 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3043 /* break out of internal loop, ala ESCAPE */
3044 break;
3047 TranslateMessage( &msg );
3048 mt.pt = msg.pt;
3050 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3051 enterIdleSent=FALSE;
3053 fRemove = FALSE;
3054 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3057 * Use the mouse coordinates in lParam instead of those in the MSG
3058 * struct to properly handle synthetic messages. They are already
3059 * in screen coordinates.
3061 mt.pt.x = (short)LOWORD(msg.lParam);
3062 mt.pt.y = (short)HIWORD(msg.lParam);
3064 /* Find a menu for this mouse event */
3065 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3067 switch(msg.message)
3069 /* no WM_NC... messages in captured state */
3071 case WM_RBUTTONDBLCLK:
3072 case WM_RBUTTONDOWN:
3073 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3074 /* fall through */
3075 case WM_LBUTTONDBLCLK:
3076 case WM_LBUTTONDOWN:
3077 /* If the message belongs to the menu, removes it from the queue */
3078 /* Else, end menu tracking */
3079 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3080 fEndMenu = !fRemove;
3081 break;
3083 case WM_RBUTTONUP:
3084 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3085 /* fall through */
3086 case WM_LBUTTONUP:
3087 /* Check if a menu was selected by the mouse */
3088 if (hmenu)
3090 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3091 TRACE("executedMenuId %d\n", executedMenuId);
3093 /* End the loop if executedMenuId is an item ID */
3094 /* or if the job was done (executedMenuId = 0). */
3095 fEndMenu = fRemove = (executedMenuId != -1);
3097 /* No menu was selected by the mouse */
3098 /* if the function was called by TrackPopupMenu, continue
3099 with the menu tracking. If not, stop it */
3100 else
3101 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3103 break;
3105 case WM_MOUSEMOVE:
3106 /* the selected menu item must be changed every time */
3107 /* the mouse moves. */
3109 if (hmenu)
3110 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3112 } /* switch(msg.message) - mouse */
3114 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3116 fRemove = TRUE; /* Keyboard messages are always removed */
3117 switch(msg.message)
3119 case WM_KEYDOWN:
3120 case WM_SYSKEYDOWN:
3121 switch(msg.wParam)
3123 case VK_MENU:
3124 fEndMenu = TRUE;
3125 break;
3127 case VK_HOME:
3128 case VK_END:
3129 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3130 NO_SELECTED_ITEM, FALSE, 0 );
3131 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3132 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3133 break;
3135 case VK_UP:
3136 case VK_DOWN: /* If on menu bar, pull-down the menu */
3138 menu = MENU_GetMenu( mt.hCurrentMenu );
3139 if (!(menu->wFlags & MF_POPUP))
3140 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3141 else /* otherwise try to move selection */
3142 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3143 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3144 break;
3146 case VK_LEFT:
3147 MENU_KeyLeft( &mt, wFlags );
3148 break;
3150 case VK_RIGHT:
3151 MENU_KeyRight( &mt, wFlags );
3152 break;
3154 case VK_ESCAPE:
3155 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3156 break;
3158 case VK_F1:
3160 HELPINFO hi;
3161 hi.cbSize = sizeof(HELPINFO);
3162 hi.iContextType = HELPINFO_MENUITEM;
3163 if (menu->FocusedItem == NO_SELECTED_ITEM)
3164 hi.iCtrlId = 0;
3165 else
3166 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3167 hi.hItemHandle = hmenu;
3168 hi.dwContextId = menu->dwContextHelpID;
3169 hi.MousePos = msg.pt;
3170 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3171 break;
3174 default:
3175 break;
3177 break; /* WM_KEYDOWN */
3179 case WM_CHAR:
3180 case WM_SYSCHAR:
3182 UINT pos;
3184 if (msg.wParam == '\r' || msg.wParam == ' ')
3186 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3187 fEndMenu = (executedMenuId != -2);
3189 break;
3192 /* Hack to avoid control chars. */
3193 /* We will find a better way real soon... */
3194 if (msg.wParam < 32) break;
3196 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3197 LOWORD(msg.wParam), FALSE );
3198 if (pos == (UINT)-2) fEndMenu = TRUE;
3199 else if (pos == (UINT)-1) MessageBeep(0);
3200 else
3202 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3203 TRUE, 0 );
3204 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3205 fEndMenu = (executedMenuId != -2);
3208 break;
3209 } /* switch(msg.message) - kbd */
3211 else
3213 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3214 DispatchMessageW( &msg );
3215 continue;
3218 if (!fEndMenu) fRemove = TRUE;
3220 /* finally remove message from the queue */
3222 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3223 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3224 else mt.trackFlags &= ~TF_SKIPREMOVE;
3226 __FINALLY( release_capture )
3228 /* If dropdown is still painted and the close box is clicked on
3229 then the menu will be destroyed as part of the DispatchMessage above.
3230 This will then invalidate the menu handle in mt.hTopMenu. We should
3231 check for this first. */
3232 if( IsMenu( mt.hTopMenu ) )
3234 menu = MENU_GetMenu( mt.hTopMenu );
3236 if( IsWindow( mt.hOwnerWnd ) )
3238 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3240 if (menu && (menu->wFlags & MF_POPUP))
3242 DestroyWindow( menu->hWnd );
3243 menu->hWnd = 0;
3245 if (!(wFlags & TPM_NONOTIFY))
3246 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3247 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3249 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3250 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3253 /* Reset the variable for hiding menu */
3254 if( menu ) menu->bTimeToHide = FALSE;
3257 /* The return value is only used by TrackPopupMenu */
3258 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3259 if (executedMenuId == -1) executedMenuId = 0;
3260 return executedMenuId;
3263 /***********************************************************************
3264 * MENU_InitTracking
3266 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3268 POPUPMENU *menu;
3270 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3272 HideCaret(0);
3274 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3275 if (!(wFlags & TPM_NONOTIFY))
3276 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3278 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3280 if (!(wFlags & TPM_NONOTIFY))
3282 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3283 /* If an app changed/recreated menu bar entries in WM_INITMENU
3284 * menu sizes will be recalculated once the menu created/shown.
3288 /* This makes the menus of applications built with Delphi work.
3289 * It also enables menus to be displayed in more than one window,
3290 * but there are some bugs left that need to be fixed in this case.
3292 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3294 return TRUE;
3296 /***********************************************************************
3297 * MENU_ExitTracking
3299 static BOOL MENU_ExitTracking(HWND hWnd)
3301 TRACE("hwnd=%p\n", hWnd);
3303 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3304 ShowCaret(0);
3305 top_popup = 0;
3306 top_popup_hmenu = NULL;
3307 return TRUE;
3310 /***********************************************************************
3311 * MENU_TrackMouseMenuBar
3313 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3315 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3317 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3318 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3320 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3322 if (IsMenu(hMenu))
3324 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3325 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3326 MENU_ExitTracking(hWnd);
3331 /***********************************************************************
3332 * MENU_TrackKbdMenuBar
3334 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3336 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3338 UINT uItem = NO_SELECTED_ITEM;
3339 HMENU hTrackMenu;
3340 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3342 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3344 /* find window that has a menu */
3346 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3347 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3349 /* check if we have to track a system menu */
3351 hTrackMenu = GetMenu( hwnd );
3352 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3354 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3355 hTrackMenu = get_win_sys_menu( hwnd );
3356 uItem = 0;
3357 wParam |= HTSYSMENU; /* prevent item lookup */
3360 if (!IsMenu( hTrackMenu )) return;
3362 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3364 if( wChar && wChar != ' ' )
3366 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3367 if ( uItem >= (UINT)(-2) )
3369 if( uItem == (UINT)(-1) ) MessageBeep(0);
3370 /* schedule end of menu tracking */
3371 wFlags |= TF_ENDMENU;
3372 goto track_menu;
3376 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3378 if (!(wParam & HTSYSMENU) || wChar == ' ')
3380 if( uItem == NO_SELECTED_ITEM )
3381 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3382 else
3383 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3386 track_menu:
3387 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3388 MENU_ExitTracking( hwnd );
3391 /**********************************************************************
3392 * TrackPopupMenuEx (USER32.@)
3394 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3395 HWND hWnd, LPTPMPARAMS lpTpm )
3397 BOOL ret = FALSE;
3399 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3400 hMenu, wFlags, x, y, hWnd, lpTpm,
3401 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3403 /* Parameter check */
3404 /* FIXME: this check is performed several times, here and in the called
3405 functions. That could be optimized */
3406 if (!MENU_GetMenu( hMenu ))
3408 SetLastError( ERROR_INVALID_MENU_HANDLE );
3409 return FALSE;
3412 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3414 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3415 if (!(wFlags & TPM_NONOTIFY))
3416 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3418 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3419 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3420 lpTpm ? &lpTpm->rcExclude : NULL );
3421 MENU_ExitTracking(hWnd);
3423 return ret;
3426 /**********************************************************************
3427 * TrackPopupMenu (USER32.@)
3429 * Like the win32 API, the function return the command ID only if the
3430 * flag TPM_RETURNCMD is on.
3433 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3434 INT nReserved, HWND hWnd, const RECT *lpRect )
3436 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3439 /***********************************************************************
3440 * PopupMenuWndProc
3442 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3444 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3446 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3448 switch(message)
3450 case WM_CREATE:
3452 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3453 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3454 return 0;
3457 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3458 return MA_NOACTIVATE;
3460 case WM_PAINT:
3462 PAINTSTRUCT ps;
3463 BeginPaint( hwnd, &ps );
3464 MENU_DrawPopupMenu( hwnd, ps.hdc,
3465 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3466 EndPaint( hwnd, &ps );
3467 return 0;
3469 case WM_ERASEBKGND:
3470 return 1;
3472 case WM_DESTROY:
3473 /* zero out global pointer in case resident popup window was destroyed. */
3474 if (hwnd == top_popup) {
3475 top_popup = 0;
3476 top_popup_hmenu = NULL;
3478 break;
3480 case WM_SHOWWINDOW:
3482 if( wParam )
3484 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3486 else
3487 SetWindowLongPtrW( hwnd, 0, 0 );
3488 break;
3490 case MM_SETMENUHANDLE:
3491 SetWindowLongPtrW( hwnd, 0, wParam );
3492 break;
3494 case MM_GETMENUHANDLE:
3495 return GetWindowLongPtrW( hwnd, 0 );
3497 default:
3498 return DefWindowProcW( hwnd, message, wParam, lParam );
3500 return 0;
3504 /***********************************************************************
3505 * MENU_GetMenuBarHeight
3507 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3509 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3510 INT orgX, INT orgY )
3512 HDC hdc;
3513 RECT rectBar;
3514 LPPOPUPMENU lppop;
3516 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3518 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3520 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3521 SelectObject( hdc, get_menu_font(FALSE));
3522 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3523 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3524 ReleaseDC( hwnd, hdc );
3525 return lppop->Height;
3529 /*******************************************************************
3530 * ChangeMenuA (USER32.@)
3532 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3533 UINT id, UINT flags )
3535 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3536 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3537 id, data );
3538 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3539 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3540 id, data );
3541 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3542 flags & MF_BYPOSITION ? pos : id,
3543 flags & ~MF_REMOVE );
3544 /* Default: MF_INSERT */
3545 return InsertMenuA( hMenu, pos, flags, id, data );
3549 /*******************************************************************
3550 * ChangeMenuW (USER32.@)
3552 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3553 UINT id, UINT flags )
3555 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3556 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3557 id, data );
3558 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3559 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3560 id, data );
3561 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3562 flags & MF_BYPOSITION ? pos : id,
3563 flags & ~MF_REMOVE );
3564 /* Default: MF_INSERT */
3565 return InsertMenuW( hMenu, pos, flags, id, data );
3569 /*******************************************************************
3570 * CheckMenuItem (USER32.@)
3572 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3574 MENUITEM *item;
3575 DWORD ret;
3577 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3578 ret = item->fState & MF_CHECKED;
3579 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3580 else item->fState &= ~MF_CHECKED;
3581 return ret;
3585 /**********************************************************************
3586 * EnableMenuItem (USER32.@)
3588 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3590 UINT oldflags;
3591 MENUITEM *item;
3592 POPUPMENU *menu;
3594 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3596 /* Get the Popupmenu to access the owner menu */
3597 if (!(menu = MENU_GetMenu(hMenu)))
3598 return (UINT)-1;
3600 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3601 return (UINT)-1;
3603 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3604 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3606 /* If the close item in the system menu change update the close button */
3607 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3609 if (menu->hSysMenuOwner != 0)
3611 RECT rc;
3612 POPUPMENU* parentMenu;
3614 /* Get the parent menu to access*/
3615 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3616 return (UINT)-1;
3618 /* Refresh the frame to reflect the change */
3619 GetWindowRect(parentMenu->hWnd, &rc);
3620 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3621 rc.bottom = 0;
3622 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3626 return oldflags;
3630 /*******************************************************************
3631 * GetMenuStringA (USER32.@)
3633 INT WINAPI GetMenuStringA(
3634 HMENU hMenu, /* [in] menuhandle */
3635 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3636 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3637 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3638 UINT wFlags /* [in] MF_ flags */
3640 MENUITEM *item;
3642 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3643 if (str && nMaxSiz) str[0] = '\0';
3644 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3645 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3646 return 0;
3648 if (!item->text) return 0;
3649 if (!str || !nMaxSiz) return strlenW(item->text);
3650 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3651 str[nMaxSiz-1] = 0;
3652 TRACE("returning %s\n", debugstr_a(str));
3653 return strlen(str);
3657 /*******************************************************************
3658 * GetMenuStringW (USER32.@)
3660 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3661 LPWSTR str, INT nMaxSiz, UINT wFlags )
3663 MENUITEM *item;
3665 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3666 if (str && nMaxSiz) str[0] = '\0';
3667 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3668 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3669 return 0;
3671 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3672 if( !(item->text)) {
3673 str[0] = 0;
3674 return 0;
3676 lstrcpynW( str, item->text, nMaxSiz );
3677 TRACE("returning %s\n", debugstr_w(str));
3678 return strlenW(str);
3682 /**********************************************************************
3683 * HiliteMenuItem (USER32.@)
3685 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3686 UINT wHilite )
3688 LPPOPUPMENU menu;
3689 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3690 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3691 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3692 if (menu->FocusedItem == wItemID) return TRUE;
3693 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3694 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3695 return TRUE;
3699 /**********************************************************************
3700 * GetMenuState (USER32.@)
3702 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3704 MENUITEM *item;
3705 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3706 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3707 debug_print_menuitem (" item: ", item, "");
3708 if (item->fType & MF_POPUP)
3710 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3711 if (!menu) return -1;
3712 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3714 else
3716 /* We used to (from way back then) mask the result to 0xff. */
3717 /* I don't know why and it seems wrong as the documented */
3718 /* return flag MF_SEPARATOR is outside that mask. */
3719 return (item->fType | item->fState);
3724 /**********************************************************************
3725 * GetMenuItemCount (USER32.@)
3727 INT WINAPI GetMenuItemCount( HMENU hMenu )
3729 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3730 if (!menu) return -1;
3731 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3732 return menu->nItems;
3736 /**********************************************************************
3737 * GetMenuItemID (USER32.@)
3739 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3741 MENUITEM * lpmi;
3743 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3744 if (lpmi->fType & MF_POPUP) return -1;
3745 return lpmi->wID;
3750 /**********************************************************************
3751 * MENU_mnu2mnuii
3753 * Uses flags, id and text ptr, passed by InsertMenu() and
3754 * ModifyMenu() to setup a MenuItemInfo structure.
3756 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3757 LPMENUITEMINFOW pmii)
3759 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3760 pmii->cbSize = sizeof( MENUITEMINFOW);
3761 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3762 /* setting bitmap clears text and vice versa */
3763 if( IS_STRING_ITEM(flags)) {
3764 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3765 if( !str)
3766 flags |= MF_SEPARATOR;
3767 /* Item beginning with a backspace is a help item */
3768 /* FIXME: wrong place, this is only true in win16 */
3769 else if( *str == '\b') {
3770 flags |= MF_HELP;
3771 str++;
3773 pmii->dwTypeData = (LPWSTR)str;
3774 } else if( flags & MFT_BITMAP){
3775 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3776 pmii->hbmpItem = HBITMAP_32(LOWORD(str));
3778 if( flags & MF_OWNERDRAW){
3779 pmii->fMask |= MIIM_DATA;
3780 pmii->dwItemData = (ULONG_PTR) str;
3782 if( flags & MF_POPUP) {
3783 pmii->fMask |= MIIM_SUBMENU;
3784 pmii->hSubMenu = (HMENU)id;
3786 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3787 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3788 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3789 pmii->wID = (UINT)id;
3793 /*******************************************************************
3794 * InsertMenuW (USER32.@)
3796 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3797 UINT_PTR id, LPCWSTR str )
3799 MENUITEM *item;
3800 MENUITEMINFOW mii;
3802 if (IS_STRING_ITEM(flags) && str)
3803 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3804 hMenu, pos, flags, id, debugstr_w(str) );
3805 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3806 hMenu, pos, flags, id, str );
3808 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3809 MENU_mnu2mnuii( flags, id, str, &mii);
3810 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3812 RemoveMenu( hMenu, pos, flags );
3813 return FALSE;
3816 item->hCheckBit = item->hUnCheckBit = 0;
3817 return TRUE;
3821 /*******************************************************************
3822 * InsertMenuA (USER32.@)
3824 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3825 UINT_PTR id, LPCSTR str )
3827 BOOL ret = FALSE;
3829 if (IS_STRING_ITEM(flags) && str)
3831 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3832 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3833 if (newstr)
3835 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3836 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3837 HeapFree( GetProcessHeap(), 0, newstr );
3839 return ret;
3841 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3845 /*******************************************************************
3846 * AppendMenuA (USER32.@)
3848 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3849 UINT_PTR id, LPCSTR data )
3851 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3855 /*******************************************************************
3856 * AppendMenuW (USER32.@)
3858 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3859 UINT_PTR id, LPCWSTR data )
3861 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3865 /**********************************************************************
3866 * RemoveMenu (USER32.@)
3868 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3870 LPPOPUPMENU menu;
3871 MENUITEM *item;
3873 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3874 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3875 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3877 /* Remove item */
3879 MENU_FreeItemData( item );
3881 if (--menu->nItems == 0)
3883 HeapFree( GetProcessHeap(), 0, menu->items );
3884 menu->items = NULL;
3886 else
3888 while(nPos < menu->nItems)
3890 *item = *(item+1);
3891 item++;
3892 nPos++;
3894 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3895 menu->nItems * sizeof(MENUITEM) );
3897 return TRUE;
3901 /**********************************************************************
3902 * DeleteMenu (USER32.@)
3904 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3906 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3907 if (!item) return FALSE;
3908 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3909 /* nPos is now the position of the item */
3910 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3911 return TRUE;
3915 /*******************************************************************
3916 * ModifyMenuW (USER32.@)
3918 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3919 UINT_PTR id, LPCWSTR str )
3921 MENUITEM *item;
3922 MENUITEMINFOW mii;
3924 if (IS_STRING_ITEM(flags))
3925 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3926 else
3927 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3929 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3930 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3931 MENU_mnu2mnuii( flags, id, str, &mii);
3932 return SetMenuItemInfo_common( item, &mii, TRUE);
3936 /*******************************************************************
3937 * ModifyMenuA (USER32.@)
3939 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3940 UINT_PTR id, LPCSTR str )
3942 BOOL ret = FALSE;
3944 if (IS_STRING_ITEM(flags) && str)
3946 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3947 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3948 if (newstr)
3950 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3951 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3952 HeapFree( GetProcessHeap(), 0, newstr );
3954 return ret;
3956 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3960 /**********************************************************************
3961 * CreatePopupMenu (USER32.@)
3963 HMENU WINAPI CreatePopupMenu(void)
3965 HMENU hmenu;
3966 POPUPMENU *menu;
3968 if (!(hmenu = CreateMenu())) return 0;
3969 menu = MENU_GetMenu( hmenu );
3970 menu->wFlags |= MF_POPUP;
3971 menu->bTimeToHide = FALSE;
3972 return hmenu;
3976 /**********************************************************************
3977 * GetMenuCheckMarkDimensions (USER.417)
3978 * GetMenuCheckMarkDimensions (USER32.@)
3980 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3982 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3986 /**********************************************************************
3987 * SetMenuItemBitmaps (USER32.@)
3989 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3990 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3992 MENUITEM *item;
3994 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3996 if (!hNewCheck && !hNewUnCheck)
3998 item->fState &= ~MF_USECHECKBITMAPS;
4000 else /* Install new bitmaps */
4002 item->hCheckBit = hNewCheck;
4003 item->hUnCheckBit = hNewUnCheck;
4004 item->fState |= MF_USECHECKBITMAPS;
4006 return TRUE;
4010 /**********************************************************************
4011 * CreateMenu (USER32.@)
4013 HMENU WINAPI CreateMenu(void)
4015 HMENU hMenu;
4016 LPPOPUPMENU menu;
4017 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4018 menu = USER_HEAP_LIN_ADDR(hMenu);
4020 ZeroMemory(menu, sizeof(POPUPMENU));
4021 menu->wMagic = MENU_MAGIC;
4022 menu->FocusedItem = NO_SELECTED_ITEM;
4023 menu->bTimeToHide = FALSE;
4025 TRACE("return %p\n", hMenu );
4027 return hMenu;
4031 /**********************************************************************
4032 * DestroyMenu (USER32.@)
4034 BOOL WINAPI DestroyMenu( HMENU hMenu )
4036 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4038 TRACE("(%p)\n", hMenu);
4041 if (!lppop) return FALSE;
4043 lppop->wMagic = 0; /* Mark it as destroyed */
4045 /* DestroyMenu should not destroy system menu popup owner */
4046 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4048 DestroyWindow( lppop->hWnd );
4049 lppop->hWnd = 0;
4052 if (lppop->items) /* recursively destroy submenus */
4054 int i;
4055 MENUITEM *item = lppop->items;
4056 for (i = lppop->nItems; i > 0; i--, item++)
4058 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4059 MENU_FreeItemData( item );
4061 HeapFree( GetProcessHeap(), 0, lppop->items );
4063 USER_HEAP_FREE( hMenu );
4064 return TRUE;
4068 /**********************************************************************
4069 * GetSystemMenu (USER32.@)
4071 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4073 WND *wndPtr = WIN_GetPtr( hWnd );
4074 HMENU retvalue = 0;
4076 if (wndPtr == WND_DESKTOP) return 0;
4077 if (wndPtr == WND_OTHER_PROCESS)
4079 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4081 else if (wndPtr)
4083 if (wndPtr->hSysMenu && bRevert)
4085 DestroyMenu(wndPtr->hSysMenu);
4086 wndPtr->hSysMenu = 0;
4089 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4090 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4092 if( wndPtr->hSysMenu )
4094 POPUPMENU *menu;
4095 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4097 /* Store the dummy sysmenu handle to facilitate the refresh */
4098 /* of the close button if the SC_CLOSE item change */
4099 menu = MENU_GetMenu(retvalue);
4100 if ( menu )
4101 menu->hSysMenuOwner = wndPtr->hSysMenu;
4103 WIN_ReleasePtr( wndPtr );
4105 return bRevert ? 0 : retvalue;
4109 /*******************************************************************
4110 * SetSystemMenu (USER32.@)
4112 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4114 WND *wndPtr = WIN_GetPtr( hwnd );
4116 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4118 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4119 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4120 WIN_ReleasePtr( wndPtr );
4121 return TRUE;
4123 return FALSE;
4127 /**********************************************************************
4128 * GetMenu (USER32.@)
4130 HMENU WINAPI GetMenu( HWND hWnd )
4132 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4133 TRACE("for %p returning %p\n", hWnd, retvalue);
4134 return retvalue;
4137 /**********************************************************************
4138 * GetMenuBarInfo (USER32.@)
4140 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4142 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4143 return FALSE;
4146 /**********************************************************************
4147 * MENU_SetMenu
4149 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4150 * SetWindowPos call that would result if SetMenu were called directly.
4152 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4154 TRACE("(%p, %p);\n", hWnd, hMenu);
4156 if (hMenu && !IsMenu(hMenu))
4158 WARN("hMenu %p is not a menu handle\n", hMenu);
4159 return FALSE;
4161 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4162 return FALSE;
4164 hWnd = WIN_GetFullHandle( hWnd );
4165 if (GetCapture() == hWnd)
4166 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4168 if (hMenu != 0)
4170 LPPOPUPMENU lpmenu;
4172 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4174 lpmenu->hWnd = hWnd;
4175 lpmenu->Height = 0; /* Make sure we recalculate the size */
4177 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4178 return TRUE;
4182 /**********************************************************************
4183 * SetMenu (USER32.@)
4185 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4187 if(!MENU_SetMenu(hWnd, hMenu))
4188 return FALSE;
4190 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4191 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4192 return TRUE;
4196 /**********************************************************************
4197 * GetSubMenu (USER32.@)
4199 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4201 MENUITEM * lpmi;
4203 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4204 if (!(lpmi->fType & MF_POPUP)) return 0;
4205 return lpmi->hSubMenu;
4209 /**********************************************************************
4210 * DrawMenuBar (USER32.@)
4212 BOOL WINAPI DrawMenuBar( HWND hWnd )
4214 LPPOPUPMENU lppop;
4215 HMENU hMenu = GetMenu(hWnd);
4217 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4218 return FALSE;
4219 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4221 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4222 lppop->hwndOwner = hWnd;
4223 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4224 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4225 return TRUE;
4228 /***********************************************************************
4229 * DrawMenuBarTemp (USER32.@)
4231 * UNDOCUMENTED !!
4233 * called by W98SE desk.cpl Control Panel Applet
4235 * Not 100% sure about the param names, but close.
4237 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4239 LPPOPUPMENU lppop;
4240 UINT i,retvalue;
4241 HFONT hfontOld = 0;
4242 BOOL flat_menu = FALSE;
4244 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4246 if (!hMenu)
4247 hMenu = GetMenu(hwnd);
4249 if (!hFont)
4250 hFont = get_menu_font(FALSE);
4252 lppop = MENU_GetMenu( hMenu );
4253 if (lppop == NULL || lprect == NULL)
4255 retvalue = GetSystemMetrics(SM_CYMENU);
4256 goto END;
4259 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4261 hfontOld = SelectObject( hDC, hFont);
4263 if (lppop->Height == 0)
4264 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4266 lprect->bottom = lprect->top + lppop->Height;
4268 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4270 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4271 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4272 LineTo( hDC, lprect->right, lprect->bottom );
4274 if (lppop->nItems == 0)
4276 retvalue = GetSystemMetrics(SM_CYMENU);
4277 goto END;
4280 for (i = 0; i < lppop->nItems; i++)
4282 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4283 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4285 retvalue = lppop->Height;
4287 END:
4288 if (hfontOld) SelectObject (hDC, hfontOld);
4289 return retvalue;
4292 /***********************************************************************
4293 * EndMenu (USER.187)
4294 * EndMenu (USER32.@)
4296 BOOL WINAPI EndMenu(void)
4298 /* if we are in the menu code, and it is active */
4299 if (!fEndMenu && top_popup)
4301 /* terminate the menu handling code */
4302 fEndMenu = TRUE;
4304 /* needs to be posted to wakeup the internal menu handler */
4305 /* which will now terminate the menu, in the event that */
4306 /* the main window was minimized, or lost focus, so we */
4307 /* don't end up with an orphaned menu */
4308 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4310 return fEndMenu;
4314 /***********************************************************************
4315 * LookupMenuHandle (USER.217)
4317 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4319 HMENU hmenu32 = HMENU_32(hmenu);
4320 UINT id32 = id;
4321 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4322 else return HMENU_16(hmenu32);
4326 /**********************************************************************
4327 * LoadMenu (USER.150)
4329 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4331 HRSRC16 hRsrc;
4332 HGLOBAL16 handle;
4333 HMENU16 hMenu;
4335 if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4336 if (!name) return 0;
4338 instance = GetExePtr( instance );
4339 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4340 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4341 hMenu = LoadMenuIndirect16(LockResource16(handle));
4342 FreeResource16( handle );
4343 return hMenu;
4347 /*****************************************************************
4348 * LoadMenuA (USER32.@)
4350 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4352 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4353 if (!hrsrc) return 0;
4354 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4358 /*****************************************************************
4359 * LoadMenuW (USER32.@)
4361 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4363 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4364 if (!hrsrc) return 0;
4365 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4369 /**********************************************************************
4370 * LoadMenuIndirect (USER.220)
4372 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4374 HMENU hMenu;
4375 WORD version, offset;
4376 LPCSTR p = template;
4378 TRACE("(%p)\n", template );
4379 version = GET_WORD(p);
4380 p += sizeof(WORD);
4381 if (version)
4383 WARN("version must be 0 for Win16\n" );
4384 return 0;
4386 offset = GET_WORD(p);
4387 p += sizeof(WORD) + offset;
4388 if (!(hMenu = CreateMenu())) return 0;
4389 if (!MENU_ParseResource( p, hMenu, FALSE ))
4391 DestroyMenu( hMenu );
4392 return 0;
4394 return HMENU_16(hMenu);
4398 /**********************************************************************
4399 * LoadMenuIndirectW (USER32.@)
4401 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4403 HMENU hMenu;
4404 WORD version, offset;
4405 LPCSTR p = template;
4407 version = GET_WORD(p);
4408 p += sizeof(WORD);
4409 TRACE("%p, ver %d\n", template, version );
4410 switch (version)
4412 case 0: /* standard format is version of 0 */
4413 offset = GET_WORD(p);
4414 p += sizeof(WORD) + offset;
4415 if (!(hMenu = CreateMenu())) return 0;
4416 if (!MENU_ParseResource( p, hMenu, TRUE ))
4418 DestroyMenu( hMenu );
4419 return 0;
4421 return hMenu;
4422 case 1: /* extended format is version of 1 */
4423 offset = GET_WORD(p);
4424 p += sizeof(WORD) + offset;
4425 if (!(hMenu = CreateMenu())) return 0;
4426 if (!MENUEX_ParseResource( p, hMenu))
4428 DestroyMenu( hMenu );
4429 return 0;
4431 return hMenu;
4432 default:
4433 ERR("version %d not supported.\n", version);
4434 return 0;
4439 /**********************************************************************
4440 * LoadMenuIndirectA (USER32.@)
4442 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4444 return LoadMenuIndirectW( template );
4448 /**********************************************************************
4449 * IsMenu (USER32.@)
4451 BOOL WINAPI IsMenu(HMENU hmenu)
4453 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4455 if (!menu)
4457 SetLastError(ERROR_INVALID_MENU_HANDLE);
4458 return FALSE;
4460 return TRUE;
4463 /**********************************************************************
4464 * GetMenuItemInfo_common
4467 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4468 LPMENUITEMINFOW lpmii, BOOL unicode)
4470 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4472 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4474 if (!menu) {
4475 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4476 return FALSE;
4479 if( lpmii->fMask & MIIM_TYPE) {
4480 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4481 WARN("invalid combination of fMask bits used\n");
4482 /* this does not happen on Win9x/ME */
4483 SetLastError( ERROR_INVALID_PARAMETER);
4484 return FALSE;
4486 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4487 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4488 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4489 if( lpmii->fType & MFT_BITMAP) {
4490 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4491 lpmii->cch = 0;
4492 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4493 /* this does not happen on Win9x/ME */
4494 lpmii->dwTypeData = 0;
4495 lpmii->cch = 0;
4499 /* copy the text string */
4500 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4501 if( !menu->text ) {
4502 if(lpmii->dwTypeData && lpmii->cch) {
4503 lpmii->cch = 0;
4504 if( unicode)
4505 *((WCHAR *)lpmii->dwTypeData) = 0;
4506 else
4507 *((CHAR *)lpmii->dwTypeData) = 0;
4509 } else {
4510 int len;
4511 if (unicode)
4513 len = strlenW(menu->text);
4514 if(lpmii->dwTypeData && lpmii->cch)
4515 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4517 else
4519 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4520 0, NULL, NULL ) - 1;
4521 if(lpmii->dwTypeData && lpmii->cch)
4522 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4523 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4524 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4526 /* if we've copied a substring we return its length */
4527 if(lpmii->dwTypeData && lpmii->cch)
4528 if (lpmii->cch <= len + 1)
4529 lpmii->cch--;
4530 else
4531 lpmii->cch = len;
4532 else {
4533 /* return length of string */
4534 /* not on Win9x/ME if fType & MFT_BITMAP */
4535 lpmii->cch = len;
4540 if (lpmii->fMask & MIIM_FTYPE)
4541 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4543 if (lpmii->fMask & MIIM_BITMAP)
4544 lpmii->hbmpItem = menu->hbmpItem;
4546 if (lpmii->fMask & MIIM_STATE)
4547 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4549 if (lpmii->fMask & MIIM_ID)
4550 lpmii->wID = menu->wID;
4552 if (lpmii->fMask & MIIM_SUBMENU)
4553 lpmii->hSubMenu = menu->hSubMenu;
4554 else {
4555 /* hSubMenu is always cleared
4556 * (not on Win9x/ME ) */
4557 lpmii->hSubMenu = 0;
4560 if (lpmii->fMask & MIIM_CHECKMARKS) {
4561 lpmii->hbmpChecked = menu->hCheckBit;
4562 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4564 if (lpmii->fMask & MIIM_DATA)
4565 lpmii->dwItemData = menu->dwItemData;
4567 return TRUE;
4570 /**********************************************************************
4571 * GetMenuItemInfoA (USER32.@)
4573 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4574 LPMENUITEMINFOA lpmii)
4576 BOOL ret;
4577 MENUITEMINFOA mii;
4578 if( lpmii->cbSize != sizeof( mii) &&
4579 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4580 SetLastError( ERROR_INVALID_PARAMETER);
4581 return FALSE;
4583 memcpy( &mii, lpmii, lpmii->cbSize);
4584 mii.cbSize = sizeof( mii);
4585 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4586 (LPMENUITEMINFOW)&mii, FALSE);
4587 mii.cbSize = lpmii->cbSize;
4588 memcpy( lpmii, &mii, mii.cbSize);
4589 return ret;
4592 /**********************************************************************
4593 * GetMenuItemInfoW (USER32.@)
4595 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4596 LPMENUITEMINFOW lpmii)
4598 BOOL ret;
4599 MENUITEMINFOW mii;
4600 if( lpmii->cbSize != sizeof( mii) &&
4601 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4602 SetLastError( ERROR_INVALID_PARAMETER);
4603 return FALSE;
4605 memcpy( &mii, lpmii, lpmii->cbSize);
4606 mii.cbSize = sizeof( mii);
4607 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4608 mii.cbSize = lpmii->cbSize;
4609 memcpy( lpmii, &mii, mii.cbSize);
4610 return ret;
4614 /* set a menu item text from a ASCII or Unicode string */
4615 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4617 if (!text)
4618 menu->text = NULL;
4619 else if (unicode)
4621 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4622 strcpyW( menu->text, text );
4624 else
4626 LPCSTR str = (LPCSTR)text;
4627 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4628 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4629 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4634 /**********************************************************************
4635 * MENU_depth
4637 * detect if there are loops in the menu tree (or the depth is too large)
4639 static int MENU_depth( POPUPMENU *pmenu, int depth)
4641 int i;
4642 MENUITEM *item;
4644 depth++;
4645 if( depth > MAXMENUDEPTH) return depth;
4646 item = pmenu->items;
4647 for( i = 0; i < pmenu->nItems && depth <= MAXMENUDEPTH; i++, item++){
4648 POPUPMENU *psubmenu = MENU_GetMenu( item->hSubMenu);
4649 if( psubmenu){
4650 int bdepth = MENU_depth( psubmenu, depth);
4651 if( bdepth > depth) depth = bdepth;
4654 return depth;
4658 /**********************************************************************
4659 * SetMenuItemInfo_common
4661 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4662 * MIIM_BITMAP and MIIM_STRING flags instead.
4665 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4666 const MENUITEMINFOW *lpmii,
4667 BOOL unicode)
4669 if (!menu) return FALSE;
4671 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4673 if (lpmii->fMask & MIIM_FTYPE ) {
4674 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4675 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4677 if (lpmii->fMask & MIIM_STRING ) {
4678 /* free the string when used */
4679 HeapFree(GetProcessHeap(), 0, menu->text);
4680 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4683 if (lpmii->fMask & MIIM_STATE)
4684 /* Other menu items having MFS_DEFAULT are not converted
4685 to normal items */
4686 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4688 if (lpmii->fMask & MIIM_ID)
4689 menu->wID = lpmii->wID;
4691 if (lpmii->fMask & MIIM_SUBMENU) {
4692 menu->hSubMenu = lpmii->hSubMenu;
4693 if (menu->hSubMenu) {
4694 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4695 if (subMenu) {
4696 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4697 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4698 menu->hSubMenu = 0;
4699 return FALSE;
4701 subMenu->wFlags |= MF_POPUP;
4702 menu->fType |= MF_POPUP;
4703 } else {
4704 SetLastError( ERROR_INVALID_PARAMETER);
4705 return FALSE;
4708 else
4709 menu->fType &= ~MF_POPUP;
4712 if (lpmii->fMask & MIIM_CHECKMARKS)
4714 menu->hCheckBit = lpmii->hbmpChecked;
4715 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4717 if (lpmii->fMask & MIIM_DATA)
4718 menu->dwItemData = lpmii->dwItemData;
4720 if (lpmii->fMask & MIIM_BITMAP)
4721 menu->hbmpItem = lpmii->hbmpItem;
4723 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4724 menu->fType |= MFT_SEPARATOR;
4726 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4727 return TRUE;
4730 /**********************************************************************
4731 * MENU_NormalizeMenuItemInfoStruct
4733 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4734 * check, copy and extend the MENUITEMINFO struct from the version that the application
4735 * supplied to the version used by wine source. */
4736 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4737 MENUITEMINFOW *pmii_out )
4739 /* do we recognize the size? */
4740 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4741 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4742 SetLastError( ERROR_INVALID_PARAMETER);
4743 return FALSE;
4745 /* copy the fields that we have */
4746 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4747 /* if the hbmpItem member is missing then extend */
4748 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4749 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4750 pmii_out->hbmpItem = NULL;
4752 /* test for invalid bit combinations */
4753 if( (pmii_out->fMask & MIIM_TYPE &&
4754 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4755 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4756 WARN("invalid combination of fMask bits used\n");
4757 /* this does not happen on Win9x/ME */
4758 SetLastError( ERROR_INVALID_PARAMETER);
4759 return FALSE;
4761 /* convert old style (MIIM_TYPE) to the new */
4762 if( pmii_out->fMask & MIIM_TYPE){
4763 pmii_out->fMask |= MIIM_FTYPE;
4764 if( IS_STRING_ITEM(pmii_out->fType)){
4765 pmii_out->fMask |= MIIM_STRING;
4766 } else if( (pmii_out->fType) & MFT_BITMAP){
4767 pmii_out->fMask |= MIIM_BITMAP;
4768 pmii_out->hbmpItem = HBITMAP_32(LOWORD(pmii_out->dwTypeData));
4771 return TRUE;
4774 /**********************************************************************
4775 * SetMenuItemInfoA (USER32.@)
4777 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4778 const MENUITEMINFOA *lpmii)
4780 MENUITEMINFOW mii;
4782 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4784 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4786 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4787 &mii, FALSE);
4790 /**********************************************************************
4791 * SetMenuItemInfoW (USER32.@)
4793 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4794 const MENUITEMINFOW *lpmii)
4796 MENUITEMINFOW mii;
4798 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4800 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4801 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4802 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4805 /**********************************************************************
4806 * SetMenuDefaultItem (USER32.@)
4809 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4811 UINT i;
4812 POPUPMENU *menu;
4813 MENUITEM *item;
4815 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4817 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4819 /* reset all default-item flags */
4820 item = menu->items;
4821 for (i = 0; i < menu->nItems; i++, item++)
4823 item->fState &= ~MFS_DEFAULT;
4826 /* no default item */
4827 if ( -1 == uItem)
4829 return TRUE;
4832 item = menu->items;
4833 if ( bypos )
4835 if ( uItem >= menu->nItems ) return FALSE;
4836 item[uItem].fState |= MFS_DEFAULT;
4837 return TRUE;
4839 else
4841 for (i = 0; i < menu->nItems; i++, item++)
4843 if (item->wID == uItem)
4845 item->fState |= MFS_DEFAULT;
4846 return TRUE;
4851 return FALSE;
4854 /**********************************************************************
4855 * GetMenuDefaultItem (USER32.@)
4857 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4859 POPUPMENU *menu;
4860 MENUITEM * item;
4861 UINT i = 0;
4863 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4865 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4867 /* find default item */
4868 item = menu->items;
4870 /* empty menu */
4871 if (! item) return -1;
4873 while ( !( item->fState & MFS_DEFAULT ) )
4875 i++; item++;
4876 if (i >= menu->nItems ) return -1;
4879 /* default: don't return disabled items */
4880 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4882 /* search rekursiv when needed */
4883 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4885 UINT ret;
4886 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4887 if ( -1 != ret ) return ret;
4889 /* when item not found in submenu, return the popup item */
4891 return ( bypos ) ? i : item->wID;
4896 /**********************************************************************
4897 * InsertMenuItemA (USER32.@)
4899 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4900 const MENUITEMINFOA *lpmii)
4902 MENUITEM *item;
4903 MENUITEMINFOW mii;
4905 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4907 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4909 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4910 return SetMenuItemInfo_common(item, &mii, FALSE);
4914 /**********************************************************************
4915 * InsertMenuItemW (USER32.@)
4917 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4918 const MENUITEMINFOW *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( lpmii, &mii )) return FALSE;
4927 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4928 return SetMenuItemInfo_common(item, &mii, TRUE);
4931 /**********************************************************************
4932 * CheckMenuRadioItem (USER32.@)
4935 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4936 UINT first, UINT last, UINT check,
4937 UINT bypos)
4939 BOOL done = FALSE;
4940 UINT i;
4941 MENUITEM *mi_first = NULL, *mi_check;
4942 HMENU m_first, m_check;
4944 for (i = first; i <= last; i++)
4946 UINT pos = i;
4948 if (!mi_first)
4950 m_first = hMenu;
4951 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4952 if (!mi_first) continue;
4953 mi_check = mi_first;
4954 m_check = m_first;
4956 else
4958 m_check = hMenu;
4959 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4960 if (!mi_check) continue;
4963 if (m_first != m_check) continue;
4964 if (mi_check->fType == MFT_SEPARATOR) continue;
4966 if (i == check)
4968 mi_check->fType |= MFT_RADIOCHECK;
4969 mi_check->fState |= MFS_CHECKED;
4970 done = TRUE;
4972 else
4974 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4975 mi_check->fState &= ~MFS_CHECKED;
4979 return done;
4983 /**********************************************************************
4984 * GetMenuItemRect (USER32.@)
4986 * ATTENTION: Here, the returned values in rect are the screen
4987 * coordinates of the item just like if the menu was
4988 * always on the upper left side of the application.
4991 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4992 LPRECT rect)
4994 POPUPMENU *itemMenu;
4995 MENUITEM *item;
4996 HWND referenceHwnd;
4998 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5000 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5001 referenceHwnd = hwnd;
5003 if(!hwnd)
5005 itemMenu = MENU_GetMenu(hMenu);
5006 if (itemMenu == NULL)
5007 return FALSE;
5009 if(itemMenu->hWnd == 0)
5010 return FALSE;
5011 referenceHwnd = itemMenu->hWnd;
5014 if ((rect == NULL) || (item == NULL))
5015 return FALSE;
5017 *rect = item->rect;
5019 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5021 return TRUE;
5024 /**********************************************************************
5025 * SetMenuInfo (USER32.@)
5027 * FIXME
5028 * actually use the items to draw the menu
5029 * (recalculate and/or redraw)
5031 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5033 POPUPMENU *menu;
5034 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5036 if (lpmi->fMask & MIM_BACKGROUND)
5037 menu->hbrBack = lpmi->hbrBack;
5039 if (lpmi->fMask & MIM_HELPID)
5040 menu->dwContextHelpID = lpmi->dwContextHelpID;
5042 if (lpmi->fMask & MIM_MAXHEIGHT)
5043 menu->cyMax = lpmi->cyMax;
5045 if (lpmi->fMask & MIM_MENUDATA)
5046 menu->dwMenuData = lpmi->dwMenuData;
5048 if (lpmi->fMask & MIM_STYLE)
5049 menu->dwStyle = lpmi->dwStyle;
5051 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5052 int i;
5053 MENUITEM *item = menu->items;
5054 for( i = menu->nItems; i; i--, item++)
5055 if( item->fType & MF_POPUP)
5056 menu_SetMenuInfo( item->hSubMenu, lpmi);
5058 return TRUE;
5061 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5063 TRACE("(%p %p)\n", hMenu, lpmi);
5064 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5065 if( lpmi->fMask & MIM_STYLE) {
5066 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5067 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5068 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5070 return TRUE;
5072 SetLastError( ERROR_INVALID_PARAMETER);
5073 return FALSE;
5076 /**********************************************************************
5077 * GetMenuInfo (USER32.@)
5079 * NOTES
5080 * win98/NT5.0
5083 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5084 { POPUPMENU *menu;
5086 TRACE("(%p %p)\n", hMenu, lpmi);
5088 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5091 if (lpmi->fMask & MIM_BACKGROUND)
5092 lpmi->hbrBack = menu->hbrBack;
5094 if (lpmi->fMask & MIM_HELPID)
5095 lpmi->dwContextHelpID = menu->dwContextHelpID;
5097 if (lpmi->fMask & MIM_MAXHEIGHT)
5098 lpmi->cyMax = menu->cyMax;
5100 if (lpmi->fMask & MIM_MENUDATA)
5101 lpmi->dwMenuData = menu->dwMenuData;
5103 if (lpmi->fMask & MIM_STYLE)
5104 lpmi->dwStyle = menu->dwStyle;
5106 return TRUE;
5108 SetLastError( ERROR_INVALID_PARAMETER);
5109 return FALSE;
5113 /**********************************************************************
5114 * SetMenuContextHelpId (USER32.@)
5116 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5118 LPPOPUPMENU menu;
5120 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5122 if ((menu = MENU_GetMenu(hMenu)))
5124 menu->dwContextHelpID = dwContextHelpID;
5125 return TRUE;
5127 return FALSE;
5131 /**********************************************************************
5132 * GetMenuContextHelpId (USER32.@)
5134 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5136 LPPOPUPMENU menu;
5138 TRACE("(%p)\n", hMenu);
5140 if ((menu = MENU_GetMenu(hMenu)))
5142 return menu->dwContextHelpID;
5144 return 0;
5147 /**********************************************************************
5148 * MenuItemFromPoint (USER32.@)
5150 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5152 POPUPMENU *menu = MENU_GetMenu(hMenu);
5153 UINT pos;
5155 /*FIXME: Do we have to handle hWnd here? */
5156 if (!menu) return -1;
5157 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5158 return pos;
5162 /**********************************************************************
5163 * translate_accelerator
5165 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5166 BYTE fVirt, WORD key, WORD cmd )
5168 INT mask = 0;
5169 UINT mesg = 0;
5171 if (wParam != key) return FALSE;
5173 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5174 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5175 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5177 if (message == WM_CHAR || message == WM_SYSCHAR)
5179 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5181 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5182 goto found;
5185 else
5187 if(fVirt & FVIRTKEY)
5189 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5190 wParam, 0xff & HIWORD(lParam));
5192 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5193 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5195 else
5197 if (!(lParam & 0x01000000)) /* no special_key */
5199 if ((fVirt & FALT) && (lParam & 0x20000000))
5200 { /* ^^ ALT pressed */
5201 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5202 goto found;
5207 return FALSE;
5209 found:
5210 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5211 mesg = 1;
5212 else
5214 HMENU hMenu, hSubMenu, hSysMenu;
5215 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5217 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5218 hSysMenu = get_win_sys_menu( hWnd );
5220 /* find menu item and ask application to initialize it */
5221 /* 1. in the system menu */
5222 hSubMenu = hSysMenu;
5223 nPos = cmd;
5224 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5226 if (GetCapture())
5227 mesg = 2;
5228 if (!IsWindowEnabled(hWnd))
5229 mesg = 3;
5230 else
5232 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5233 if(hSubMenu != hSysMenu)
5235 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5236 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5237 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5239 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5242 else /* 2. in the window's menu */
5244 hSubMenu = hMenu;
5245 nPos = cmd;
5246 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5248 if (GetCapture())
5249 mesg = 2;
5250 if (!IsWindowEnabled(hWnd))
5251 mesg = 3;
5252 else
5254 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5255 if(hSubMenu != hMenu)
5257 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5258 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5259 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5261 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5266 if (mesg == 0)
5268 if (uSysStat != (UINT)-1)
5270 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5271 mesg=4;
5272 else
5273 mesg=WM_SYSCOMMAND;
5275 else
5277 if (uStat != (UINT)-1)
5279 if (IsIconic(hWnd))
5280 mesg=5;
5281 else
5283 if (uStat & (MF_DISABLED|MF_GRAYED))
5284 mesg=6;
5285 else
5286 mesg=WM_COMMAND;
5289 else
5290 mesg=WM_COMMAND;
5295 if( mesg==WM_COMMAND )
5297 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5298 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5300 else if( mesg==WM_SYSCOMMAND )
5302 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5303 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5305 else
5307 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5308 * #0: unknown (please report!)
5309 * #1: for WM_KEYUP,WM_SYSKEYUP
5310 * #2: mouse is captured
5311 * #3: window is disabled
5312 * #4: it's a disabled system menu option
5313 * #5: it's a menu option, but window is iconic
5314 * #6: it's a menu option, but disabled
5316 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5317 if(mesg==0)
5318 ERR_(accel)(" unknown reason - please report!\n");
5320 return TRUE;
5323 /**********************************************************************
5324 * TranslateAcceleratorA (USER32.@)
5325 * TranslateAccelerator (USER32.@)
5327 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5329 /* YES, Accel16! */
5330 LPACCEL16 lpAccelTbl;
5331 int i;
5332 WPARAM wParam;
5334 if (!hWnd || !msg) return 0;
5336 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5338 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5339 return 0;
5342 wParam = msg->wParam;
5344 switch (msg->message)
5346 case WM_KEYDOWN:
5347 case WM_SYSKEYDOWN:
5348 break;
5350 case WM_CHAR:
5351 case WM_SYSCHAR:
5353 char ch = LOWORD(wParam);
5354 WCHAR wch;
5355 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5356 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5358 break;
5360 default:
5361 return 0;
5364 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5365 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5366 i = 0;
5369 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5370 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5371 return 1;
5372 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5374 return 0;
5377 /**********************************************************************
5378 * TranslateAcceleratorW (USER32.@)
5380 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5382 /* YES, Accel16! */
5383 LPACCEL16 lpAccelTbl;
5384 int i;
5386 if (!hWnd || !msg) return 0;
5388 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5390 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5391 return 0;
5394 switch (msg->message)
5396 case WM_KEYDOWN:
5397 case WM_SYSKEYDOWN:
5398 case WM_CHAR:
5399 case WM_SYSCHAR:
5400 break;
5402 default:
5403 return 0;
5406 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5407 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5408 i = 0;
5411 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5412 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5413 return 1;
5414 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5416 return 0;