push 43f03fe87c2254c6df67b2de3c08b5b20fd64327
[wine/hacks.git] / dlls / user32 / menu.c
blob1df3675f21a49e43657db54ee84e30c02cd15bab
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_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
141 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
142 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
144 /* Space between 2 columns */
145 #define MENU_COL_SPACE 4
147 /* top and bottom margins for popup menus */
148 #define MENU_TOP_MARGIN 3
149 #define MENU_BOTTOM_MARGIN 2
151 /* (other menu->FocusedItem values give the position of the focused item) */
152 #define NO_SELECTED_ITEM 0xffff
154 #define MENU_ITEM_TYPE(flags) \
155 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
157 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
158 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
159 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
161 #define IS_SYSTEM_MENU(menu) \
162 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
164 #define MENUITEMINFO_TYPE_MASK \
165 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
166 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
167 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
168 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
169 #define STATE_MASK (~TYPE_MASK)
170 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
172 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
174 static SIZE menucharsize;
175 static UINT ODitemheight; /* default owner drawn item height */
177 /* Use global popup window because there's no way 2 menus can
178 * be tracked at the same time. */
179 static HWND top_popup;
181 /* Flag set by EndMenu() to force an exit from menu tracking */
182 static BOOL fEndMenu = FALSE;
184 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class =
193 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
194 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
195 NULL, /* procA (winproc is Unicode only) */
196 PopupMenuWndProc, /* procW */
197 sizeof(HMENU), /* extra */
198 IDC_ARROW, /* cursor */
199 (HBRUSH)(COLOR_MENU+1) /* brush */
203 /***********************************************************************
204 * debug_print_menuitem
206 * Print a menuitem in readable form.
209 #define debug_print_menuitem(pre, mp, post) \
210 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
212 #define MENUOUT(text) \
213 TRACE("%s%s", (count++ ? "," : ""), (text))
215 #define MENUFLAG(bit,text) \
216 do { \
217 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
218 } while (0)
220 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
221 const char *postfix)
223 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
224 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
225 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
226 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
227 TRACE("%s ", prefix);
228 if (mp) {
229 UINT flags = mp->fType;
230 TRACE( "{ ID=0x%lx", mp->wID);
231 if ( mp->hSubMenu)
232 TRACE( ", Sub=%p", mp->hSubMenu);
233 if (flags) {
234 int count = 0;
235 TRACE( ", fType=");
236 MENUFLAG( MFT_SEPARATOR, "sep");
237 MENUFLAG( MFT_OWNERDRAW, "own");
238 MENUFLAG( MFT_BITMAP, "bit");
239 MENUFLAG(MF_POPUP, "pop");
240 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
241 MENUFLAG(MFT_MENUBREAK, "brk");
242 MENUFLAG(MFT_RADIOCHECK, "radio");
243 MENUFLAG(MFT_RIGHTORDER, "rorder");
244 MENUFLAG(MF_SYSMENU, "sys");
245 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 if (flags)
247 TRACE( "+0x%x", flags);
249 flags = mp->fState;
250 if (flags) {
251 int count = 0;
252 TRACE( ", State=");
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
260 if (flags)
261 TRACE( "+0x%x", flags);
263 if (mp->hCheckBit)
264 TRACE( ", Chk=%p", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 TRACE( ", Unc=%p", mp->hUnCheckBit);
267 if (mp->text)
268 TRACE( ", Text=%s", debugstr_w(mp->text));
269 if (mp->dwItemData)
270 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
271 if (mp->hbmpItem)
273 if( IS_MAGIC_BITMAP(mp->hbmpItem))
274 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
275 else
276 TRACE( ", hbitmap=%p", mp->hbmpItem);
278 TRACE( " }");
279 } else
280 TRACE( "NULL");
281 TRACE(" %s\n", postfix);
284 #undef MENUOUT
285 #undef MENUFLAG
288 /***********************************************************************
289 * MENU_GetMenu
291 * Validate the given menu handle and returns the menu structure pointer.
293 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
295 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
296 if (!menu || menu->wMagic != MENU_MAGIC)
298 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
299 menu = NULL;
301 return menu;
304 /***********************************************************************
305 * get_win_sys_menu
307 * Get the system menu of a window
309 static HMENU get_win_sys_menu( HWND hwnd )
311 HMENU ret = 0;
312 WND *win = WIN_GetPtr( hwnd );
313 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
315 ret = win->hSysMenu;
316 WIN_ReleasePtr( win );
318 return ret;
321 /***********************************************************************
322 * get_menu_font
324 static HFONT get_menu_font( BOOL bold )
326 static HFONT hMenuFont, hMenuFontBold;
328 HFONT ret = bold ? hMenuFontBold : hMenuFont;
330 if (!ret)
332 NONCLIENTMETRICSW ncm;
333 HFONT prev;
335 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
336 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
338 if (bold)
340 ncm.lfMenuFont.lfWeight += 300;
341 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
343 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
344 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
345 ret, NULL );
346 if (prev)
348 /* another thread beat us to it */
349 DeleteObject( ret );
350 ret = prev;
353 return ret;
356 /***********************************************************************
357 * get_arrow_bitmap
359 static HBITMAP get_arrow_bitmap(void)
361 static HBITMAP arrow_bitmap;
363 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
364 return arrow_bitmap;
367 /***********************************************************************
368 * get_down_arrow_bitmap
370 static HBITMAP get_down_arrow_bitmap(void)
372 static HBITMAP arrow_bitmap;
374 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
375 return arrow_bitmap;
378 /***********************************************************************
379 * get_down_arrow_inactive_bitmap
381 static HBITMAP get_down_arrow_inactive_bitmap(void)
383 static HBITMAP arrow_bitmap;
385 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
386 return arrow_bitmap;
389 /***********************************************************************
390 * get_up_arrow_bitmap
392 static HBITMAP get_up_arrow_bitmap(void)
394 static HBITMAP arrow_bitmap;
396 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
397 return arrow_bitmap;
400 /***********************************************************************
401 * get_up_arrow_inactive_bitmap
403 static HBITMAP get_up_arrow_inactive_bitmap(void)
405 static HBITMAP arrow_bitmap;
407 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
408 return arrow_bitmap;
411 /***********************************************************************
412 * MENU_CopySysPopup
414 * Return the default system menu.
416 static HMENU MENU_CopySysPopup(void)
418 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
419 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
421 if( hMenu ) {
422 MENUINFO minfo;
423 MENUITEMINFOW miteminfo;
424 POPUPMENU* menu = MENU_GetMenu(hMenu);
425 menu->wFlags |= MF_SYSMENU | MF_POPUP;
426 minfo.cbSize = sizeof( MENUINFO);
427 minfo.dwStyle = MNS_CHECKORBMP;
428 minfo.fMask = MIM_STYLE;
429 SetMenuInfo( hMenu, &minfo);
430 miteminfo.cbSize = sizeof( MENUITEMINFOW);
431 miteminfo.fMask = MIIM_BITMAP;
432 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
433 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
434 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
435 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
436 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
437 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
438 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
439 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
440 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
442 else
443 ERR("Unable to load default system menu\n" );
445 TRACE("returning %p.\n", hMenu );
447 return hMenu;
451 /**********************************************************************
452 * MENU_GetSysMenu
454 * Create a copy of the system menu. System menu in Windows is
455 * a special menu bar with the single entry - system menu popup.
456 * This popup is presented to the outside world as a "system menu".
457 * However, the real system menu handle is sometimes seen in the
458 * WM_MENUSELECT parameters (and Word 6 likes it this way).
460 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
462 HMENU hMenu;
464 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
465 if ((hMenu = CreateMenu()))
467 POPUPMENU *menu = MENU_GetMenu(hMenu);
468 menu->wFlags = MF_SYSMENU;
469 menu->hWnd = WIN_GetFullHandle( hWnd );
470 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
472 if (!hPopupMenu)
473 hPopupMenu = MENU_CopySysPopup();
475 if (hPopupMenu)
477 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
478 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
480 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
481 (UINT_PTR)hPopupMenu, NULL );
483 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
484 menu->items[0].fState = 0;
485 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
487 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
488 return hMenu;
490 DestroyMenu( hMenu );
492 ERR("failed to load system menu!\n");
493 return 0;
497 /***********************************************************************
498 * MENU_InitSysMenuPopup
500 * Grey the appropriate items in System menu.
502 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
504 BOOL gray;
506 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
507 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
508 gray = ((style & WS_MAXIMIZE) != 0);
509 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
510 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
511 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
512 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
513 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
514 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
515 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
516 gray = (clsStyle & CS_NOCLOSE) != 0;
518 /* The menu item must keep its state if it's disabled */
519 if(gray)
520 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
524 /******************************************************************************
526 * UINT MENU_GetStartOfNextColumn(
527 * HMENU hMenu )
529 *****************************************************************************/
531 static UINT MENU_GetStartOfNextColumn(
532 HMENU hMenu )
534 POPUPMENU *menu = MENU_GetMenu(hMenu);
535 UINT i;
537 if(!menu)
538 return NO_SELECTED_ITEM;
540 i = menu->FocusedItem + 1;
541 if( i == NO_SELECTED_ITEM )
542 return i;
544 for( ; i < menu->nItems; ++i ) {
545 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
546 return i;
549 return NO_SELECTED_ITEM;
553 /******************************************************************************
555 * UINT MENU_GetStartOfPrevColumn(
556 * HMENU hMenu )
558 *****************************************************************************/
560 static UINT MENU_GetStartOfPrevColumn(
561 HMENU hMenu )
563 POPUPMENU *menu = MENU_GetMenu(hMenu);
564 UINT i;
566 if( !menu )
567 return NO_SELECTED_ITEM;
569 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
570 return NO_SELECTED_ITEM;
572 /* Find the start of the column */
574 for(i = menu->FocusedItem; i != 0 &&
575 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
576 --i); /* empty */
578 if(i == 0)
579 return NO_SELECTED_ITEM;
581 for(--i; i != 0; --i) {
582 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
583 break;
586 TRACE("ret %d.\n", i );
588 return i;
593 /***********************************************************************
594 * MENU_FindItem
596 * Find a menu item. Return a pointer on the item, and modifies *hmenu
597 * in case the item was in a sub-menu.
599 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
601 POPUPMENU *menu;
602 MENUITEM *fallback = NULL;
603 UINT fallback_pos = 0;
604 UINT i;
606 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
607 if (wFlags & MF_BYPOSITION)
609 if (*nPos >= menu->nItems) return NULL;
610 return &menu->items[*nPos];
612 else
614 MENUITEM *item = menu->items;
615 for (i = 0; i < menu->nItems; i++, item++)
617 if (item->fType & MF_POPUP)
619 HMENU hsubmenu = item->hSubMenu;
620 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
621 if (subitem)
623 *hmenu = hsubmenu;
624 return subitem;
626 else if (item->wID == *nPos)
628 /* fallback to this item if nothing else found */
629 fallback_pos = i;
630 fallback = item;
633 else if (item->wID == *nPos)
635 *nPos = i;
636 return item;
641 if (fallback)
642 *nPos = fallback_pos;
644 return fallback;
647 /***********************************************************************
648 * MENU_FindSubMenu
650 * Find a Sub menu. Return the position of the submenu, and modifies
651 * *hmenu in case it is found in another sub-menu.
652 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
654 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
656 POPUPMENU *menu;
657 UINT i;
658 MENUITEM *item;
659 if (((*hmenu)==(HMENU)0xffff) ||
660 (!(menu = MENU_GetMenu(*hmenu))))
661 return NO_SELECTED_ITEM;
662 item = menu->items;
663 for (i = 0; i < menu->nItems; i++, item++) {
664 if(!(item->fType & MF_POPUP)) continue;
665 if (item->hSubMenu == hSubTarget) {
666 return i;
668 else {
669 HMENU hsubmenu = item->hSubMenu;
670 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
671 if (pos != NO_SELECTED_ITEM) {
672 *hmenu = hsubmenu;
673 return pos;
677 return NO_SELECTED_ITEM;
680 /***********************************************************************
681 * MENU_FreeItemData
683 static void MENU_FreeItemData( MENUITEM* item )
685 /* delete text */
686 HeapFree( GetProcessHeap(), 0, item->text );
689 /***********************************************************************
690 * MENU_AdjustMenuItemRect
692 * Adjust menu item rectangle according to scrolling state.
694 static void
695 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
697 if (menu->bScrolling)
699 UINT arrow_bitmap_height;
700 BITMAP bmp;
702 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
703 arrow_bitmap_height = bmp.bmHeight;
704 rect->top += arrow_bitmap_height - menu->nScrollPos;
705 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
710 /***********************************************************************
711 * MENU_FindItemByCoords
713 * Find the item at the specified coordinates (screen coords). Does
714 * not work for child windows and therefore should not be called for
715 * an arbitrary system menu.
717 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
718 POINT pt, UINT *pos )
720 MENUITEM *item;
721 UINT i;
722 RECT rect;
724 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
725 pt.x -= rect.left;
726 pt.y -= rect.top;
727 item = menu->items;
728 for (i = 0; i < menu->nItems; i++, item++)
730 rect = item->rect;
731 MENU_AdjustMenuItemRect(menu, &rect);
732 if (PtInRect(&rect, pt))
734 if (pos) *pos = i;
735 return item;
738 return NULL;
742 /***********************************************************************
743 * MENU_FindItemByKey
745 * Find the menu item selected by a key press.
746 * Return item id, -1 if none, -2 if we should close the menu.
748 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
749 WCHAR key, BOOL forceMenuChar )
751 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
753 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
755 if (hmenu)
757 POPUPMENU *menu = MENU_GetMenu( hmenu );
758 MENUITEM *item = menu->items;
759 LRESULT menuchar;
761 if( !forceMenuChar )
763 UINT i;
765 for (i = 0; i < menu->nItems; i++, item++)
767 if( item->text)
769 WCHAR *p = item->text - 2;
772 p = strchrW (p + 2, '&');
774 while (p != NULL && p [1] == '&');
775 if (p && (toupperW(p[1]) == toupperW(key))) return i;
779 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
780 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
781 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
782 if (HIWORD(menuchar) == 1) return (UINT)(-2);
784 return (UINT)(-1);
788 /***********************************************************************
789 * MENU_GetBitmapItemSize
791 * Get the size of a bitmap item.
793 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
794 HWND hwndOwner)
796 BITMAP bm;
797 HBITMAP bmp = lpitem->hbmpItem;
799 size->cx = size->cy = 0;
801 /* check if there is a magic menu item associated with this item */
802 switch( (INT_PTR) bmp )
804 case (INT_PTR)HBMMENU_CALLBACK:
806 MEASUREITEMSTRUCT measItem;
807 measItem.CtlType = ODT_MENU;
808 measItem.CtlID = 0;
809 measItem.itemID = lpitem->wID;
810 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
811 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
812 measItem.itemData = lpitem->dwItemData;
813 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
814 size->cx = measItem.itemWidth;
815 size->cy = measItem.itemHeight;
816 return;
818 break;
819 case (INT_PTR)HBMMENU_SYSTEM:
820 if (lpitem->dwItemData)
822 bmp = (HBITMAP)lpitem->dwItemData;
823 break;
825 /* fall through */
826 case (INT_PTR)HBMMENU_MBAR_RESTORE:
827 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
828 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
829 case (INT_PTR)HBMMENU_MBAR_CLOSE:
830 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
831 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
832 size->cy = size->cx;
833 return;
834 case (INT_PTR)HBMMENU_POPUP_CLOSE:
835 case (INT_PTR)HBMMENU_POPUP_RESTORE:
836 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
837 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
838 size->cx = GetSystemMetrics( SM_CYMENU ) - 4; /* FIXME: test */
839 size->cy = size->cx;
840 return;
842 if (GetObjectW(bmp, sizeof(bm), &bm ))
844 size->cx = bm.bmWidth;
845 size->cy = bm.bmHeight;
849 /***********************************************************************
850 * MENU_DrawBitmapItem
852 * Draw a bitmap item.
854 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
855 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
857 BITMAP bm;
858 DWORD rop;
859 HDC hdcMem;
860 HBITMAP bmp;
861 int w = rect->right - rect->left;
862 int h = rect->bottom - rect->top;
863 int bmp_xoffset = 0;
864 int left, top;
865 HBITMAP hbmToDraw = lpitem->hbmpItem;
866 bmp = hbmToDraw;
868 /* Check if there is a magic menu item associated with this item */
869 if (IS_MAGIC_BITMAP(hbmToDraw))
871 UINT flags = 0;
872 WCHAR bmchr = 0;
873 RECT r;
875 switch((INT_PTR)hbmToDraw)
877 case (INT_PTR)HBMMENU_SYSTEM:
878 if (lpitem->dwItemData)
880 bmp = (HBITMAP)lpitem->dwItemData;
881 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
883 else
885 static HBITMAP hBmpSysMenu;
887 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
888 bmp = hBmpSysMenu;
889 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
890 /* only use right half of the bitmap */
891 bmp_xoffset = bm.bmWidth / 2;
892 bm.bmWidth -= bmp_xoffset;
894 goto got_bitmap;
895 case (INT_PTR)HBMMENU_MBAR_RESTORE:
896 flags = DFCS_CAPTIONRESTORE;
897 break;
898 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
899 flags = DFCS_CAPTIONMIN;
900 break;
901 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
902 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
903 break;
904 case (INT_PTR)HBMMENU_MBAR_CLOSE:
905 flags = DFCS_CAPTIONCLOSE;
906 break;
907 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
908 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
909 break;
910 case (INT_PTR)HBMMENU_CALLBACK:
912 DRAWITEMSTRUCT drawItem;
913 drawItem.CtlType = ODT_MENU;
914 drawItem.CtlID = 0;
915 drawItem.itemID = lpitem->wID;
916 drawItem.itemAction = odaction;
917 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
918 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
919 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
920 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
921 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
922 drawItem.hwndItem = (HWND)hmenu;
923 drawItem.hDC = hdc;
924 drawItem.itemData = lpitem->dwItemData;
925 drawItem.rcItem = *rect;
926 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
927 return;
929 break;
930 case (INT_PTR)HBMMENU_POPUP_CLOSE:
931 bmchr = 0x72;
932 break;
933 case (INT_PTR)HBMMENU_POPUP_RESTORE:
934 bmchr = 0x32;
935 break;
936 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
937 bmchr = 0x31;
938 break;
939 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
940 bmchr = 0x30;
941 break;
942 default:
943 FIXME("Magic %p not implemented\n", hbmToDraw);
944 return;
946 if (bmchr)
948 HFONT hfont, hfontsav;
949 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
950 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
951 { 'M','a','r','l','e','t','t',0 } };
952 logfont.lfHeight = min( h, w) - 2 ;
953 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
954 hfont = CreateFontIndirectW( &logfont);
955 hfontsav = SelectObject(hdc, hfont);
956 TextOutW( hdc, rect->left, rect->top, &bmchr, 1);
957 SelectObject(hdc, hfontsav);
958 DeleteObject( hfont);
960 else
962 r = *rect;
963 InflateRect( &r, -1, -1 );
964 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
965 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
967 return;
970 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
972 got_bitmap:
973 hdcMem = CreateCompatibleDC( hdc );
974 SelectObject( hdcMem, bmp );
976 /* handle fontsize > bitmap_height */
977 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
978 left=rect->left;
979 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
980 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
981 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
982 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
983 DeleteDC( hdcMem );
987 /***********************************************************************
988 * MENU_CalcItemSize
990 * Calculate the size of the menu item and store it in lpitem->rect.
992 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
993 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
995 WCHAR *p;
996 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
997 UINT arrow_bitmap_width;
998 BITMAP bm;
999 INT itemheight;
1001 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1002 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1003 (menuBar ? " (MenuBar)" : ""));
1005 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1006 arrow_bitmap_width = bm.bmWidth;
1008 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1009 if( !menucharsize.cx ) {
1010 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1011 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1012 * but it is unlikely an application will depend on that */
1013 ODitemheight = HIWORD( GetDialogBaseUnits());
1016 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1018 if (lpitem->fType & MF_OWNERDRAW)
1020 MEASUREITEMSTRUCT mis;
1021 mis.CtlType = ODT_MENU;
1022 mis.CtlID = 0;
1023 mis.itemID = lpitem->wID;
1024 mis.itemData = lpitem->dwItemData;
1025 mis.itemHeight = ODitemheight;
1026 mis.itemWidth = 0;
1027 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1028 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1029 * width of a menufont character to the width of an owner-drawn menu.
1031 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1032 if (menuBar) {
1033 /* under at least win95 you seem to be given a standard
1034 height for the menu and the height value is ignored */
1035 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1036 } else
1037 lpitem->rect.bottom += mis.itemHeight;
1039 TRACE("id=%04lx size=%dx%d\n",
1040 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1041 lpitem->rect.bottom-lpitem->rect.top);
1042 return;
1045 if (lpitem->fType & MF_SEPARATOR)
1047 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1048 if( !menuBar)
1049 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1050 return;
1053 itemheight = 0;
1054 lpitem->xTab = 0;
1056 if (!menuBar) {
1057 if (lpitem->hbmpItem) {
1058 SIZE size;
1060 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1061 /* Keep the size of the bitmap in callback mode to be able
1062 * to draw it correctly */
1063 lpitem->bmpsize = size;
1064 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1065 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1066 lpitem->rect.right += size.cx + 2;
1067 itemheight = size.cy + 2;
1069 if( !(lppop->dwStyle & MNS_NOCHECK))
1070 lpitem->rect.right += check_bitmap_width;
1071 lpitem->rect.right += 4 + menucharsize.cx;
1072 lpitem->xTab = lpitem->rect.right;
1073 lpitem->rect.right += arrow_bitmap_width;
1074 } else if (lpitem->hbmpItem) { /* menuBar */
1075 SIZE size;
1077 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1078 lpitem->bmpsize = size;
1079 lpitem->rect.right += size.cx;
1080 if( lpitem->text) lpitem->rect.right += 2;
1081 itemheight = size.cy;
1084 /* it must be a text item - unless it's the system menu */
1085 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1086 HFONT hfontOld = NULL;
1087 RECT rc = lpitem->rect;
1088 LONG txtheight, txtwidth;
1090 if ( lpitem->fState & MFS_DEFAULT ) {
1091 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1093 if (menuBar) {
1094 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1095 DT_SINGLELINE|DT_CALCRECT);
1096 lpitem->rect.right += rc.right - rc.left;
1097 itemheight = max( max( itemheight, txtheight),
1098 GetSystemMetrics( SM_CYMENU) - 1);
1099 lpitem->rect.right += 2 * menucharsize.cx;
1100 } else {
1101 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1102 RECT tmprc = rc;
1103 LONG tmpheight;
1104 int n = (int)( p - lpitem->text);
1105 /* Item contains a tab (only meaningful in popup menus) */
1106 /* get text size before the tab */
1107 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1108 DT_SINGLELINE|DT_CALCRECT);
1109 txtwidth = rc.right - rc.left;
1110 p += 1; /* advance past the Tab */
1111 /* get text size after the tab */
1112 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1113 DT_SINGLELINE|DT_CALCRECT);
1114 lpitem->xTab += txtwidth;
1115 txtheight = max( txtheight, tmpheight);
1116 txtwidth += menucharsize.cx + /* space for the tab */
1117 tmprc.right - tmprc.left; /* space for the short cut */
1118 } else {
1119 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1120 DT_SINGLELINE|DT_CALCRECT);
1121 txtwidth = rc.right - rc.left;
1122 lpitem->xTab += txtwidth;
1124 lpitem->rect.right += 2 + txtwidth;
1125 itemheight = max( itemheight,
1126 max( txtheight + 2, menucharsize.cy + 4));
1128 if (hfontOld) SelectObject (hdc, hfontOld);
1129 } else if( menuBar) {
1130 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1132 lpitem->rect.bottom += itemheight;
1133 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1137 /***********************************************************************
1138 * MENU_GetMaxPopupHeight
1140 static UINT
1141 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1143 if (lppop->cyMax)
1144 return lppop->cyMax;
1145 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1149 /***********************************************************************
1150 * MENU_PopupMenuCalcSize
1152 * Calculate the size of a popup menu.
1154 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1156 MENUITEM *lpitem;
1157 HDC hdc;
1158 UINT start, i;
1159 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1161 lppop->Width = lppop->Height = 0;
1162 if (lppop->nItems == 0) return;
1163 hdc = GetDC( 0 );
1165 SelectObject( hdc, get_menu_font(FALSE));
1167 start = 0;
1168 maxX = 2 + 1;
1170 lppop->maxBmpSize.cx = 0;
1171 lppop->maxBmpSize.cy = 0;
1173 while (start < lppop->nItems)
1175 lpitem = &lppop->items[start];
1176 orgX = maxX;
1177 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1178 orgX += MENU_COL_SPACE;
1179 orgY = MENU_TOP_MARGIN;
1181 maxTab = maxTabWidth = 0;
1182 /* Parse items until column break or end of menu */
1183 for (i = start; i < lppop->nItems; i++, lpitem++)
1185 if ((i != start) &&
1186 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1188 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1189 maxX = max( maxX, lpitem->rect.right );
1190 orgY = lpitem->rect.bottom;
1191 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1193 maxTab = max( maxTab, lpitem->xTab );
1194 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1198 /* Finish the column (set all items to the largest width found) */
1199 maxX = max( maxX, maxTab + maxTabWidth );
1200 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1202 lpitem->rect.right = maxX;
1203 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1204 lpitem->xTab = maxTab;
1207 lppop->Height = max( lppop->Height, orgY );
1210 lppop->Width = maxX;
1212 /* space for 3d border */
1213 lppop->Height += MENU_BOTTOM_MARGIN;
1214 lppop->Width += 2;
1216 /* Adjust popup height if it exceeds maximum */
1217 maxHeight = MENU_GetMaxPopupHeight(lppop);
1218 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1219 if (lppop->Height >= maxHeight)
1221 lppop->Height = maxHeight;
1222 lppop->bScrolling = TRUE;
1224 else
1226 lppop->bScrolling = FALSE;
1229 ReleaseDC( 0, hdc );
1233 /***********************************************************************
1234 * MENU_MenuBarCalcSize
1236 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1237 * height is off by 1 pixel which causes lengthy window relocations when
1238 * active document window is maximized/restored.
1240 * Calculate the size of the menu bar.
1242 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1243 LPPOPUPMENU lppop, HWND hwndOwner )
1245 MENUITEM *lpitem;
1246 UINT start, i, helpPos;
1247 int orgX, orgY, maxY;
1249 if ((lprect == NULL) || (lppop == NULL)) return;
1250 if (lppop->nItems == 0) return;
1251 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1252 lppop->Width = lprect->right - lprect->left;
1253 lppop->Height = 0;
1254 maxY = lprect->top+1;
1255 start = 0;
1256 helpPos = ~0U;
1257 lppop->maxBmpSize.cx = 0;
1258 lppop->maxBmpSize.cy = 0;
1259 while (start < lppop->nItems)
1261 lpitem = &lppop->items[start];
1262 orgX = lprect->left;
1263 orgY = maxY;
1265 /* Parse items until line break or end of menu */
1266 for (i = start; i < lppop->nItems; i++, lpitem++)
1268 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1269 if ((i != start) &&
1270 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1272 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1273 debug_print_menuitem (" item: ", lpitem, "");
1274 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1276 if (lpitem->rect.right > lprect->right)
1278 if (i != start) break;
1279 else lpitem->rect.right = lprect->right;
1281 maxY = max( maxY, lpitem->rect.bottom );
1282 orgX = lpitem->rect.right;
1285 /* Finish the line (set all items to the largest height found) */
1286 while (start < i) lppop->items[start++].rect.bottom = maxY;
1289 lprect->bottom = maxY;
1290 lppop->Height = lprect->bottom - lprect->top;
1292 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1293 /* the last item (if several lines, only move the last line) */
1294 if (helpPos == ~0U) return;
1295 lpitem = &lppop->items[lppop->nItems-1];
1296 orgY = lpitem->rect.top;
1297 orgX = lprect->right;
1298 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1299 if (lpitem->rect.top != orgY) break; /* Other line */
1300 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1301 lpitem->rect.left += orgX - lpitem->rect.right;
1302 lpitem->rect.right = orgX;
1303 orgX = lpitem->rect.left;
1308 /***********************************************************************
1309 * MENU_DrawScrollArrows
1311 * Draw scroll arrows.
1313 static void
1314 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1316 HDC hdcMem = CreateCompatibleDC(hdc);
1317 HBITMAP hOrigBitmap;
1318 UINT arrow_bitmap_width, arrow_bitmap_height;
1319 BITMAP bmp;
1320 RECT rect;
1322 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1323 arrow_bitmap_width = bmp.bmWidth;
1324 arrow_bitmap_height = bmp.bmHeight;
1327 if (lppop->nScrollPos)
1328 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1329 else
1330 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1331 rect.left = 0;
1332 rect.top = 0;
1333 rect.right = lppop->Width;
1334 rect.bottom = arrow_bitmap_height;
1335 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1336 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1337 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1338 rect.top = lppop->Height - arrow_bitmap_height;
1339 rect.bottom = lppop->Height;
1340 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1341 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1342 SelectObject(hdcMem, get_down_arrow_bitmap());
1343 else
1344 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1345 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1346 lppop->Height - arrow_bitmap_height,
1347 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1348 SelectObject(hdcMem, hOrigBitmap);
1349 DeleteDC(hdcMem);
1353 /***********************************************************************
1354 * draw_popup_arrow
1356 * Draws the popup-menu arrow.
1358 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1359 UINT arrow_bitmap_height)
1361 HDC hdcMem = CreateCompatibleDC( hdc );
1362 HBITMAP hOrigBitmap;
1364 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1365 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1366 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1367 arrow_bitmap_width, arrow_bitmap_height,
1368 hdcMem, 0, 0, SRCCOPY );
1369 SelectObject( hdcMem, hOrigBitmap );
1370 DeleteDC( hdcMem );
1372 /***********************************************************************
1373 * MENU_DrawMenuItem
1375 * Draw a single menu item.
1377 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1378 UINT height, BOOL menuBar, UINT odaction )
1380 RECT rect;
1381 BOOL flat_menu = FALSE;
1382 int bkgnd;
1383 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1384 POPUPMENU *menu = MENU_GetMenu(hmenu);
1385 RECT bmprc;
1387 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1389 if (!menuBar) {
1390 BITMAP bmp;
1391 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1392 arrow_bitmap_width = bmp.bmWidth;
1393 arrow_bitmap_height = bmp.bmHeight;
1396 if (lpitem->fType & MF_SYSMENU)
1398 if( !IsIconic(hwnd) )
1399 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1400 return;
1403 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1404 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1406 /* Setup colors */
1408 if (lpitem->fState & MF_HILITE)
1410 if(menuBar && !flat_menu) {
1411 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1412 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1413 } else {
1414 if(lpitem->fState & MF_GRAYED)
1415 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1416 else
1417 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1418 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1421 else
1423 if (lpitem->fState & MF_GRAYED)
1424 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1425 else
1426 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1427 SetBkColor( hdc, GetSysColor( bkgnd ) );
1430 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1431 rect = lpitem->rect;
1432 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1434 if (lpitem->fType & MF_OWNERDRAW)
1437 ** Experimentation under Windows reveals that an owner-drawn
1438 ** menu is given the rectangle which includes the space it requested
1439 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1440 ** and a popup-menu arrow. This is the value of lpitem->rect.
1441 ** Windows will leave all drawing to the application except for
1442 ** the popup-menu arrow. Windows always draws that itself, after
1443 ** the menu owner has finished drawing.
1445 DRAWITEMSTRUCT dis;
1447 dis.CtlType = ODT_MENU;
1448 dis.CtlID = 0;
1449 dis.itemID = lpitem->wID;
1450 dis.itemData = lpitem->dwItemData;
1451 dis.itemState = 0;
1452 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1453 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1454 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1455 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1456 dis.hwndItem = (HWND)hmenu;
1457 dis.hDC = hdc;
1458 dis.rcItem = rect;
1459 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1460 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1461 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1462 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1463 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1464 /* Draw the popup-menu arrow */
1465 if (lpitem->fType & MF_POPUP)
1466 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1467 arrow_bitmap_height);
1468 return;
1471 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1473 if (lpitem->fState & MF_HILITE)
1475 if (flat_menu)
1477 InflateRect (&rect, -1, -1);
1478 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1479 InflateRect (&rect, 1, 1);
1480 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1482 else
1484 if(menuBar)
1485 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1486 else
1487 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1490 else
1491 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1493 SetBkMode( hdc, TRANSPARENT );
1495 /* vertical separator */
1496 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1498 HPEN oldPen;
1499 RECT rc = rect;
1501 rc.left -= MENU_COL_SPACE / 2 + 1;
1502 rc.top = 3;
1503 rc.bottom = height - 3;
1504 if (flat_menu)
1506 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1507 MoveToEx( hdc, rc.left, rc.top, NULL );
1508 LineTo( hdc, rc.left, rc.bottom );
1509 SelectObject( hdc, oldPen );
1511 else
1512 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1515 /* horizontal separator */
1516 if (lpitem->fType & MF_SEPARATOR)
1518 HPEN oldPen;
1519 RECT rc = rect;
1521 rc.left++;
1522 rc.right--;
1523 rc.top = ( rc.top + rc.bottom) / 2;
1524 if (flat_menu)
1526 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1527 MoveToEx( hdc, rc.left, rc.top, NULL );
1528 LineTo( hdc, rc.right, rc.top );
1529 SelectObject( hdc, oldPen );
1531 else
1532 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1533 return;
1536 /* helper lines for debugging */
1537 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1538 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1539 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1540 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1543 if (lpitem->hbmpItem) {
1544 /* calculate the bitmap rectangle in coordinates relative
1545 * to the item rectangle */
1546 if( menuBar) {
1547 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1548 bmprc.left = 3;
1549 else
1550 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1552 else if (menu->dwStyle & MNS_NOCHECK)
1553 bmprc.left = 4;
1554 else if (menu->dwStyle & MNS_CHECKORBMP)
1555 bmprc.left = 2;
1556 else
1557 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1558 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1559 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1560 bmprc.top = 0;
1561 else
1562 bmprc.top = (rect.bottom - rect.top -
1563 lpitem->bmpsize.cy) / 2;
1564 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1567 if (!menuBar)
1569 HBITMAP bm;
1570 INT y = rect.top + rect.bottom;
1571 RECT rc = rect;
1572 int checked = FALSE;
1573 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1574 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1575 /* Draw the check mark
1577 * FIXME:
1578 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1580 if( !(menu->dwStyle & MNS_NOCHECK)) {
1581 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1582 lpitem->hUnCheckBit;
1583 if (bm) /* we have a custom bitmap */
1585 HDC hdcMem = CreateCompatibleDC( hdc );
1587 SelectObject( hdcMem, bm );
1588 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1589 check_bitmap_width, check_bitmap_height,
1590 hdcMem, 0, 0, SRCCOPY );
1591 DeleteDC( hdcMem );
1592 checked = TRUE;
1594 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1596 RECT r;
1597 HBITMAP bm = CreateBitmap( check_bitmap_width,
1598 check_bitmap_height, 1, 1, NULL );
1599 HDC hdcMem = CreateCompatibleDC( hdc );
1601 SelectObject( hdcMem, bm );
1602 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1603 DrawFrameControl( hdcMem, &r, DFC_MENU,
1604 (lpitem->fType & MFT_RADIOCHECK) ?
1605 DFCS_MENUBULLET : DFCS_MENUCHECK );
1606 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1607 hdcMem, 0, 0, SRCCOPY );
1608 DeleteDC( hdcMem );
1609 DeleteObject( bm );
1610 checked = TRUE;
1613 if( lpitem->hbmpItem &&
1614 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1615 POINT origorg;
1616 /* some applications make this assumption on the DC's origin */
1617 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1618 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1619 odaction, FALSE);
1620 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1622 /* Draw the popup-menu arrow */
1623 if (lpitem->fType & MF_POPUP)
1624 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1625 arrow_bitmap_height);
1626 rect.left += 4;
1627 if( !(menu->dwStyle & MNS_NOCHECK))
1628 rect.left += check_bitmap_width;
1629 rect.right -= arrow_bitmap_width;
1631 else if( lpitem->hbmpItem)
1632 { /* Draw the bitmap */
1633 POINT origorg;
1635 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1636 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1637 odaction, menuBar);
1638 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1640 /* process text if present */
1641 if (lpitem->text)
1643 register int i;
1644 HFONT hfontOld = 0;
1646 UINT uFormat = (menuBar) ?
1647 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1648 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1650 if( !(menu->dwStyle & MNS_CHECKORBMP))
1651 rect.left += menu->maxBmpSize.cx;
1653 if ( lpitem->fState & MFS_DEFAULT )
1655 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1658 if (menuBar) {
1659 if( lpitem->hbmpItem)
1660 rect.left += lpitem->bmpsize.cx;
1661 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1662 rect.left += menucharsize.cx;
1663 rect.right -= menucharsize.cx;
1666 for (i = 0; lpitem->text[i]; i++)
1667 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1668 break;
1670 if(lpitem->fState & MF_GRAYED)
1672 if (!(lpitem->fState & MF_HILITE) )
1674 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1675 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1676 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1677 --rect.left; --rect.top; --rect.right; --rect.bottom;
1679 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1682 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1684 /* paint the shortcut text */
1685 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1687 if (lpitem->text[i] == '\t')
1689 rect.left = lpitem->xTab;
1690 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1692 else
1694 rect.right = lpitem->xTab;
1695 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1698 if(lpitem->fState & MF_GRAYED)
1700 if (!(lpitem->fState & MF_HILITE) )
1702 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1703 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1704 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1705 --rect.left; --rect.top; --rect.right; --rect.bottom;
1707 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1709 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1712 if (hfontOld)
1713 SelectObject (hdc, hfontOld);
1718 /***********************************************************************
1719 * MENU_DrawPopupMenu
1721 * Paint a popup menu.
1723 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1725 HBRUSH hPrevBrush = 0;
1726 RECT rect;
1728 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1730 GetClientRect( hwnd, &rect );
1732 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1733 && (SelectObject( hdc, get_menu_font(FALSE))))
1735 HPEN hPrevPen;
1737 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1739 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1740 if( hPrevPen )
1742 POPUPMENU *menu;
1743 BOOL flat_menu = FALSE;
1745 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1746 if (flat_menu)
1747 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1748 else
1749 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1751 if( (menu = MENU_GetMenu( hmenu )))
1753 /* draw menu items */
1754 if( menu->nItems)
1756 MENUITEM *item;
1757 UINT u;
1759 item = menu->items;
1760 for( u = menu->nItems; u > 0; u--, item++)
1761 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1762 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1764 /* draw scroll arrows */
1765 if (menu->bScrolling)
1766 MENU_DrawScrollArrows(menu, hdc);
1768 } else
1770 SelectObject( hdc, hPrevBrush );
1775 /***********************************************************************
1776 * MENU_DrawMenuBar
1778 * Paint a menu bar. Returns the height of the menu bar.
1779 * called from [windows/nonclient.c]
1781 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1782 BOOL suppress_draw)
1784 LPPOPUPMENU lppop;
1785 HFONT hfontOld = 0;
1786 HMENU hMenu = GetMenu(hwnd);
1788 lppop = MENU_GetMenu( hMenu );
1789 if (lppop == NULL || lprect == NULL)
1791 return GetSystemMetrics(SM_CYMENU);
1794 if (suppress_draw)
1796 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1798 if (lppop->Height == 0)
1799 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1801 lprect->bottom = lprect->top + lppop->Height;
1803 if (hfontOld) SelectObject( hDC, hfontOld);
1804 return lppop->Height;
1806 else
1807 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1811 /***********************************************************************
1812 * MENU_ShowPopup
1814 * Display a popup menu.
1816 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1817 INT x, INT y, INT xanchor, INT yanchor )
1819 POPUPMENU *menu;
1820 INT width, height;
1821 POINT pt;
1822 HMONITOR monitor;
1823 MONITORINFO info;
1825 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1826 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1828 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1829 if (menu->FocusedItem != NO_SELECTED_ITEM)
1831 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1832 menu->FocusedItem = NO_SELECTED_ITEM;
1835 /* store the owner for DrawItem */
1836 menu->hwndOwner = hwndOwner;
1838 menu->nScrollPos = 0;
1839 MENU_PopupMenuCalcSize( menu );
1841 /* adjust popup menu pos so that it fits within the desktop */
1843 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1844 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1846 /* FIXME: should use item rect */
1847 pt.x = x;
1848 pt.y = y;
1849 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1850 info.cbSize = sizeof(info);
1851 GetMonitorInfoW( monitor, &info );
1853 if( flags & TPM_RIGHTALIGN ) x -= width;
1854 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1856 if( flags & TPM_BOTTOMALIGN ) y -= height;
1857 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1859 if( x + width > info.rcWork.right)
1861 if( xanchor && x >= width - xanchor )
1862 x -= width - xanchor;
1864 if( x + width > info.rcWork.right)
1865 x = info.rcWork.right - width;
1867 if( x < info.rcWork.left ) x = info.rcWork.left;
1869 if( y + height > info.rcWork.bottom)
1871 if( yanchor && y >= height + yanchor )
1872 y -= height + yanchor;
1874 if( y + height > info.rcWork.bottom)
1875 y = info.rcWork.bottom - height;
1877 if( y < info.rcWork.top ) y = info.rcWork.top;
1879 /* NOTE: In Windows, top menu popup is not owned. */
1880 menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1881 WS_POPUP, x, y, width, height,
1882 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1883 (LPVOID)hmenu );
1884 if( !menu->hWnd ) return FALSE;
1885 if (!top_popup) top_popup = menu->hWnd;
1887 /* Display the window */
1889 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1890 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1891 UpdateWindow( menu->hWnd );
1892 return TRUE;
1896 /***********************************************************************
1897 * MENU_EnsureMenuItemVisible
1899 static void
1900 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1902 if (lppop->bScrolling)
1904 MENUITEM *item = &lppop->items[wIndex];
1905 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1906 UINT nOldPos = lppop->nScrollPos;
1907 RECT rc;
1908 UINT arrow_bitmap_height;
1909 BITMAP bmp;
1911 GetClientRect(lppop->hWnd, &rc);
1913 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1914 arrow_bitmap_height = bmp.bmHeight;
1916 rc.top += arrow_bitmap_height;
1917 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1919 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1920 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1923 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1924 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1925 MENU_DrawScrollArrows(lppop, hdc);
1927 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1929 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1930 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1931 MENU_DrawScrollArrows(lppop, hdc);
1937 /***********************************************************************
1938 * MENU_SelectItem
1940 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1941 BOOL sendMenuSelect, HMENU topmenu )
1943 LPPOPUPMENU lppop;
1944 HDC hdc;
1946 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1948 lppop = MENU_GetMenu( hmenu );
1949 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1951 if (lppop->FocusedItem == wIndex) return;
1952 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1953 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1954 if (!top_popup) top_popup = lppop->hWnd;
1956 SelectObject( hdc, get_menu_font(FALSE));
1958 /* Clear previous highlighted item */
1959 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1961 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1962 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1963 lppop->Height, !(lppop->wFlags & MF_POPUP),
1964 ODA_SELECT );
1967 /* Highlight new item (if any) */
1968 lppop->FocusedItem = wIndex;
1969 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1971 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1972 lppop->items[wIndex].fState |= MF_HILITE;
1973 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1974 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1975 &lppop->items[wIndex], lppop->Height,
1976 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1978 if (sendMenuSelect)
1980 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1981 SendMessageW( hwndOwner, WM_MENUSELECT,
1982 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1983 ip->fType | ip->fState |
1984 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1987 else if (sendMenuSelect) {
1988 if(topmenu){
1989 int pos;
1990 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1991 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1992 MENUITEM *ip = &ptm->items[pos];
1993 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1994 ip->fType | ip->fState |
1995 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1999 ReleaseDC( lppop->hWnd, hdc );
2003 /***********************************************************************
2004 * MENU_MoveSelection
2006 * Moves currently selected item according to the offset parameter.
2007 * If there is no selection then it should select the last item if
2008 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2010 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2012 INT i;
2013 POPUPMENU *menu;
2015 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2017 menu = MENU_GetMenu( hmenu );
2018 if ((!menu) || (!menu->items)) return;
2020 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2022 if( menu->nItems == 1 ) return; else
2023 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2024 ; i += offset)
2025 if (!(menu->items[i].fType & MF_SEPARATOR))
2027 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2028 return;
2032 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2033 i >= 0 && i < menu->nItems ; i += offset)
2034 if (!(menu->items[i].fType & MF_SEPARATOR))
2036 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2037 return;
2042 /**********************************************************************
2043 * MENU_SetItemData
2045 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2046 * ModifyMenu().
2048 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2049 LPCWSTR str )
2051 debug_print_menuitem("MENU_SetItemData from: ", item, "");
2052 TRACE("flags=%x str=%p\n", flags, str);
2054 if (IS_STRING_ITEM(flags))
2056 LPWSTR prevText = item->text;
2057 if (!str)
2059 flags |= MF_SEPARATOR;
2060 item->text = NULL;
2062 else
2064 LPWSTR text;
2065 /* Item beginning with a backspace is a help item */
2066 if (*str == '\b')
2068 flags |= MF_HELP;
2069 str++;
2071 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2072 return FALSE;
2073 strcpyW( text, str );
2074 item->text = text;
2076 item->hbmpItem = NULL;
2077 HeapFree( GetProcessHeap(), 0, prevText );
2079 else if(( flags & MFT_BITMAP)) {
2080 item->hbmpItem = HBITMAP_32(LOWORD(str));
2081 /* setting bitmap clears text */
2082 HeapFree( GetProcessHeap(), 0, item->text );
2083 item->text = NULL;
2086 if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2088 if (flags & MF_OWNERDRAW)
2089 item->dwItemData = (DWORD_PTR)str;
2090 else
2091 item->dwItemData = 0;
2093 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2094 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2096 if (flags & MF_POPUP)
2098 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2099 if (menu) menu->wFlags |= MF_POPUP;
2100 else
2102 item->wID = 0;
2103 item->hSubMenu = 0;
2104 item->fType = 0;
2105 item->fState = 0;
2106 return FALSE;
2110 item->wID = id;
2111 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2113 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2114 flags |= MF_POPUP; /* keep popup */
2116 item->fType = flags & TYPE_MASK;
2117 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2118 for ModifyMenu, but Windows accepts it */
2119 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2121 /* Don't call SetRectEmpty here! */
2123 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2124 return TRUE;
2128 /**********************************************************************
2129 * MENU_InsertItem
2131 * Insert (allocate) a new item into a menu.
2133 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2135 MENUITEM *newItems;
2136 POPUPMENU *menu;
2138 if (!(menu = MENU_GetMenu(hMenu)))
2139 return NULL;
2141 /* Find where to insert new item */
2143 if (flags & MF_BYPOSITION) {
2144 if (pos > menu->nItems)
2145 pos = menu->nItems;
2146 } else {
2147 if (!MENU_FindItem( &hMenu, &pos, flags ))
2148 pos = menu->nItems;
2149 else {
2150 if (!(menu = MENU_GetMenu( hMenu )))
2151 return NULL;
2155 /* Make sure that MDI system buttons stay on the right side.
2156 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2157 * regardless of their id.
2159 while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2160 (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2161 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2162 pos--;
2164 TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2166 /* Create new items array */
2168 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2169 if (!newItems)
2171 WARN("allocation failed\n" );
2172 return NULL;
2174 if (menu->nItems > 0)
2176 /* Copy the old array into the new one */
2177 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2178 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2179 (menu->nItems-pos)*sizeof(MENUITEM) );
2180 HeapFree( GetProcessHeap(), 0, menu->items );
2182 menu->items = newItems;
2183 menu->nItems++;
2184 memset( &newItems[pos], 0, sizeof(*newItems) );
2185 menu->Height = 0; /* force size recalculate */
2186 return &newItems[pos];
2190 /**********************************************************************
2191 * MENU_ParseResource
2193 * Parse a standard menu resource and add items to the menu.
2194 * Return a pointer to the end of the resource.
2196 * NOTE: flags is equivalent to the mtOption field
2198 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2200 WORD flags, id = 0;
2201 LPCSTR str;
2202 BOOL end_flag;
2206 flags = GET_WORD(res);
2207 end_flag = flags & MF_END;
2208 /* Remove MF_END because it has the same value as MF_HILITE */
2209 flags &= ~MF_END;
2210 res += sizeof(WORD);
2211 if (!(flags & MF_POPUP))
2213 id = GET_WORD(res);
2214 res += sizeof(WORD);
2216 str = res;
2217 if (!unicode) res += strlen(str) + 1;
2218 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2219 if (flags & MF_POPUP)
2221 HMENU hSubMenu = CreatePopupMenu();
2222 if (!hSubMenu) return NULL;
2223 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2224 return NULL;
2225 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2226 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2228 else /* Not a popup */
2230 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2231 else AppendMenuW( hMenu, flags, id,
2232 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2234 } while (!end_flag);
2235 return res;
2239 /**********************************************************************
2240 * MENUEX_ParseResource
2242 * Parse an extended menu resource and add items to the menu.
2243 * Return a pointer to the end of the resource.
2245 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2247 WORD resinfo;
2248 do {
2249 MENUITEMINFOW mii;
2251 mii.cbSize = sizeof(mii);
2252 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2253 mii.fType = GET_DWORD(res);
2254 res += sizeof(DWORD);
2255 mii.fState = GET_DWORD(res);
2256 res += sizeof(DWORD);
2257 mii.wID = GET_DWORD(res);
2258 res += sizeof(DWORD);
2259 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2260 res += sizeof(WORD);
2261 /* Align the text on a word boundary. */
2262 res += (~((UINT_PTR)res - 1)) & 1;
2263 mii.dwTypeData = (LPWSTR) res;
2264 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2265 /* Align the following fields on a dword boundary. */
2266 res += (~((UINT_PTR)res - 1)) & 3;
2268 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2269 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2271 if (resinfo & 1) { /* Pop-up? */
2272 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2273 res += sizeof(DWORD);
2274 mii.hSubMenu = CreatePopupMenu();
2275 if (!mii.hSubMenu)
2276 return NULL;
2277 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2278 DestroyMenu(mii.hSubMenu);
2279 return NULL;
2281 mii.fMask |= MIIM_SUBMENU;
2282 mii.fType |= MF_POPUP;
2284 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2286 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2287 mii.wID, mii.fType);
2288 mii.fType |= MF_SEPARATOR;
2290 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2291 } while (!(resinfo & MF_END));
2292 return res;
2296 /***********************************************************************
2297 * MENU_GetSubPopup
2299 * Return the handle of the selected sub-popup menu (if any).
2301 static HMENU MENU_GetSubPopup( HMENU hmenu )
2303 POPUPMENU *menu;
2304 MENUITEM *item;
2306 menu = MENU_GetMenu( hmenu );
2308 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2310 item = &menu->items[menu->FocusedItem];
2311 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2312 return item->hSubMenu;
2313 return 0;
2317 /***********************************************************************
2318 * MENU_HideSubPopups
2320 * Hide the sub-popup menus of this menu.
2322 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2323 BOOL sendMenuSelect, UINT wFlags )
2325 POPUPMENU *menu = MENU_GetMenu( hmenu );
2327 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2329 if (menu && top_popup)
2331 HMENU hsubmenu;
2332 POPUPMENU *submenu;
2333 MENUITEM *item;
2335 if (menu->FocusedItem != NO_SELECTED_ITEM)
2337 item = &menu->items[menu->FocusedItem];
2338 if (!(item->fType & MF_POPUP) ||
2339 !(item->fState & MF_MOUSESELECT)) return;
2340 item->fState &= ~MF_MOUSESELECT;
2341 hsubmenu = item->hSubMenu;
2342 } else return;
2344 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2345 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2346 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2347 DestroyWindow( submenu->hWnd );
2348 submenu->hWnd = 0;
2350 if (!(wFlags & TPM_NONOTIFY))
2351 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2352 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2357 /***********************************************************************
2358 * MENU_ShowSubPopup
2360 * Display the sub-menu of the selected item of this menu.
2361 * Return the handle of the submenu, or hmenu if no submenu to display.
2363 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2364 BOOL selectFirst, UINT wFlags )
2366 RECT rect;
2367 POPUPMENU *menu;
2368 MENUITEM *item;
2369 HDC hdc;
2371 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2373 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2375 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2377 item = &menu->items[menu->FocusedItem];
2378 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2379 return hmenu;
2381 /* message must be sent before using item,
2382 because nearly everything may be changed by the application ! */
2384 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2385 if (!(wFlags & TPM_NONOTIFY))
2386 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2387 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2389 item = &menu->items[menu->FocusedItem];
2390 rect = item->rect;
2392 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2393 if (!(item->fState & MF_HILITE))
2395 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2396 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2398 SelectObject( hdc, get_menu_font(FALSE));
2400 item->fState |= MF_HILITE;
2401 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2402 ReleaseDC( menu->hWnd, hdc );
2404 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2405 item->rect = rect;
2407 item->fState |= MF_MOUSESELECT;
2409 if (IS_SYSTEM_MENU(menu))
2411 MENU_InitSysMenuPopup(item->hSubMenu,
2412 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2413 GetClassLongW( menu->hWnd, GCL_STYLE));
2415 NC_GetSysPopupPos( menu->hWnd, &rect );
2416 rect.top = rect.bottom;
2417 rect.right = GetSystemMetrics(SM_CXSIZE);
2418 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2420 else
2422 GetWindowRect( menu->hWnd, &rect );
2423 if (menu->wFlags & MF_POPUP)
2425 RECT rc = item->rect;
2427 MENU_AdjustMenuItemRect(menu, &rc);
2429 /* The first item in the popup menu has to be at the
2430 same y position as the focused menu item */
2431 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2432 rect.top += rc.top - MENU_TOP_MARGIN;
2433 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2434 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2435 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2437 else
2439 rect.left += item->rect.left;
2440 rect.top += item->rect.bottom;
2441 rect.right = item->rect.right - item->rect.left;
2442 rect.bottom = item->rect.bottom - item->rect.top;
2446 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2447 rect.left, rect.top, rect.right, rect.bottom );
2448 if (selectFirst)
2449 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2450 return item->hSubMenu;
2455 /**********************************************************************
2456 * MENU_IsMenuActive
2458 HWND MENU_IsMenuActive(void)
2460 return top_popup;
2463 /***********************************************************************
2464 * MENU_PtMenu
2466 * Walks menu chain trying to find a menu pt maps to.
2468 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2470 POPUPMENU *menu = MENU_GetMenu( hMenu );
2471 UINT item = menu->FocusedItem;
2472 HMENU ret;
2474 /* try subpopup first (if any) */
2475 ret = (item != NO_SELECTED_ITEM &&
2476 (menu->items[item].fType & MF_POPUP) &&
2477 (menu->items[item].fState & MF_MOUSESELECT))
2478 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2480 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2482 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2483 if( menu->wFlags & MF_POPUP )
2485 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2487 else if (ht == HTSYSMENU)
2488 ret = get_win_sys_menu( menu->hWnd );
2489 else if (ht == HTMENU)
2490 ret = GetMenu( menu->hWnd );
2492 return ret;
2495 /***********************************************************************
2496 * MENU_ExecFocusedItem
2498 * Execute a menu item (for instance when user pressed Enter).
2499 * Return the wID of the executed item. Otherwise, -1 indicating
2500 * that no menu item was executed, -2 if a popup is shown;
2501 * Have to receive the flags for the TrackPopupMenu options to avoid
2502 * sending unwanted message.
2505 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2507 MENUITEM *item;
2508 POPUPMENU *menu = MENU_GetMenu( hMenu );
2510 TRACE("%p hmenu=%p\n", pmt, hMenu);
2512 if (!menu || !menu->nItems ||
2513 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2515 item = &menu->items[menu->FocusedItem];
2517 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2519 if (!(item->fType & MF_POPUP))
2521 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2523 /* If TPM_RETURNCMD is set you return the id, but
2524 do not send a message to the owner */
2525 if(!(wFlags & TPM_RETURNCMD))
2527 if( menu->wFlags & MF_SYSMENU )
2528 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2529 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2530 else
2532 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2533 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2535 if (dwStyle & MNS_NOTIFYBYPOS)
2536 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2537 (LPARAM)hMenu);
2538 else
2539 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2542 return item->wID;
2545 else
2547 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2548 return -2;
2551 return -1;
2554 /***********************************************************************
2555 * MENU_SwitchTracking
2557 * Helper function for menu navigation routines.
2559 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2561 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2562 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2564 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2566 if( pmt->hTopMenu != hPtMenu &&
2567 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2569 /* both are top level menus (system and menu-bar) */
2570 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2571 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2572 pmt->hTopMenu = hPtMenu;
2574 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2575 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2579 /***********************************************************************
2580 * MENU_ButtonDown
2582 * Return TRUE if we can go on with menu tracking.
2584 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2586 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2588 if (hPtMenu)
2590 UINT id = 0;
2591 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2592 MENUITEM *item;
2594 if( IS_SYSTEM_MENU(ptmenu) )
2595 item = ptmenu->items;
2596 else
2597 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2599 if( item )
2601 if( ptmenu->FocusedItem != id )
2602 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2604 /* If the popup menu is not already "popped" */
2605 if(!(item->fState & MF_MOUSESELECT ))
2607 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2610 return TRUE;
2612 /* Else the click was on the menu bar, finish the tracking */
2614 return FALSE;
2617 /***********************************************************************
2618 * MENU_ButtonUp
2620 * Return the value of MENU_ExecFocusedItem if
2621 * the selected item was not a popup. Else open the popup.
2622 * A -1 return value indicates that we go on with menu tracking.
2625 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2627 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2629 if (hPtMenu)
2631 UINT id = 0;
2632 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2633 MENUITEM *item;
2635 if( IS_SYSTEM_MENU(ptmenu) )
2636 item = ptmenu->items;
2637 else
2638 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2640 if( item && (ptmenu->FocusedItem == id ))
2642 debug_print_menuitem ("FocusedItem: ", item, "");
2644 if( !(item->fType & MF_POPUP) )
2646 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2647 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2648 return executedMenuId;
2651 /* If we are dealing with the top-level menu */
2652 /* and this is a click on an already "popped" item: */
2653 /* Stop the menu tracking and close the opened submenus */
2654 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2655 return 0;
2657 ptmenu->bTimeToHide = TRUE;
2659 return -1;
2663 /***********************************************************************
2664 * MENU_MouseMove
2666 * Return TRUE if we can go on with menu tracking.
2668 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2670 UINT id = NO_SELECTED_ITEM;
2671 POPUPMENU *ptmenu = NULL;
2673 if( hPtMenu )
2675 ptmenu = MENU_GetMenu( hPtMenu );
2676 if( IS_SYSTEM_MENU(ptmenu) )
2677 id = 0;
2678 else
2679 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2682 if( id == NO_SELECTED_ITEM )
2684 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2685 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2688 else if( ptmenu->FocusedItem != id )
2690 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2691 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2693 return TRUE;
2697 /***********************************************************************
2698 * MENU_DoNextMenu
2700 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2702 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2704 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2705 BOOL atEnd = FALSE;
2707 /* When skipping left, we need to do something special after the
2708 first menu. */
2709 if (vk == VK_LEFT && menu->FocusedItem == 0)
2711 atEnd = TRUE;
2713 /* When skipping right, for the non-system menu, we need to
2714 handle the last non-special menu item (ie skip any window
2715 icons such as MDI maximize, restore or close) */
2716 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2718 UINT i = menu->FocusedItem + 1;
2719 while (i < menu->nItems) {
2720 if ((menu->items[i].wID >= SC_SIZE &&
2721 menu->items[i].wID <= SC_RESTORE)) {
2722 i++;
2723 } else break;
2725 if (i == menu->nItems) {
2726 atEnd = TRUE;
2729 /* When skipping right, we need to cater for the system menu */
2730 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2732 if (menu->FocusedItem == (menu->nItems - 1)) {
2733 atEnd = TRUE;
2737 if( atEnd )
2739 MDINEXTMENU next_menu;
2740 HMENU hNewMenu;
2741 HWND hNewWnd;
2742 UINT id = 0;
2744 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2745 next_menu.hmenuNext = 0;
2746 next_menu.hwndNext = 0;
2747 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2749 TRACE("%p [%p] -> %p [%p]\n",
2750 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2752 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2754 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2755 hNewWnd = pmt->hOwnerWnd;
2756 if( IS_SYSTEM_MENU(menu) )
2758 /* switch to the menu bar */
2760 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2762 if( vk == VK_LEFT )
2764 menu = MENU_GetMenu( hNewMenu );
2765 id = menu->nItems - 1;
2767 /* Skip backwards over any system predefined icons,
2768 eg. MDI close, restore etc icons */
2769 while ((id > 0) &&
2770 (menu->items[id].wID >= SC_SIZE &&
2771 menu->items[id].wID <= SC_RESTORE)) id--;
2774 else if (style & WS_SYSMENU )
2776 /* switch to the system menu */
2777 hNewMenu = get_win_sys_menu( hNewWnd );
2779 else return FALSE;
2781 else /* application returned a new menu to switch to */
2783 hNewMenu = next_menu.hmenuNext;
2784 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2786 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2788 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2790 if (style & WS_SYSMENU &&
2791 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2793 /* get the real system menu */
2794 hNewMenu = get_win_sys_menu(hNewWnd);
2796 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2798 /* FIXME: Not sure what to do here;
2799 * perhaps try to track hNewMenu as a popup? */
2801 TRACE(" -- got confused.\n");
2802 return FALSE;
2805 else return FALSE;
2808 if( hNewMenu != pmt->hTopMenu )
2810 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2811 FALSE, 0 );
2812 if( pmt->hCurrentMenu != pmt->hTopMenu )
2813 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2816 if( hNewWnd != pmt->hOwnerWnd )
2818 pmt->hOwnerWnd = hNewWnd;
2819 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2822 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2823 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2825 return TRUE;
2827 return FALSE;
2830 /***********************************************************************
2831 * MENU_SuspendPopup
2833 * The idea is not to show the popup if the next input message is
2834 * going to hide it anyway.
2836 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2838 MSG msg;
2840 msg.hwnd = pmt->hOwnerWnd;
2842 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2843 pmt->trackFlags |= TF_SKIPREMOVE;
2845 switch( uMsg )
2847 case WM_KEYDOWN:
2848 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2849 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2851 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2852 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2853 if( msg.message == WM_KEYDOWN &&
2854 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2856 pmt->trackFlags |= TF_SUSPENDPOPUP;
2857 return TRUE;
2860 break;
2863 /* failures go through this */
2864 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2865 return FALSE;
2868 /***********************************************************************
2869 * MENU_KeyEscape
2871 * Handle a VK_ESCAPE key event in a menu.
2873 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2875 BOOL bEndMenu = TRUE;
2877 if (pmt->hCurrentMenu != pmt->hTopMenu)
2879 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2881 if (menu->wFlags & MF_POPUP)
2883 HMENU hmenutmp, hmenuprev;
2885 hmenuprev = hmenutmp = pmt->hTopMenu;
2887 /* close topmost popup */
2888 while (hmenutmp != pmt->hCurrentMenu)
2890 hmenuprev = hmenutmp;
2891 hmenutmp = MENU_GetSubPopup( hmenuprev );
2894 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2895 pmt->hCurrentMenu = hmenuprev;
2896 bEndMenu = FALSE;
2900 return bEndMenu;
2903 /***********************************************************************
2904 * MENU_KeyLeft
2906 * Handle a VK_LEFT key event in a menu.
2908 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2910 POPUPMENU *menu;
2911 HMENU hmenutmp, hmenuprev;
2912 UINT prevcol;
2914 hmenuprev = hmenutmp = pmt->hTopMenu;
2915 menu = MENU_GetMenu( hmenutmp );
2917 /* Try to move 1 column left (if possible) */
2918 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2919 NO_SELECTED_ITEM ) {
2921 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2922 prevcol, TRUE, 0 );
2923 return;
2926 /* close topmost popup */
2927 while (hmenutmp != pmt->hCurrentMenu)
2929 hmenuprev = hmenutmp;
2930 hmenutmp = MENU_GetSubPopup( hmenuprev );
2933 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2934 pmt->hCurrentMenu = hmenuprev;
2936 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2938 /* move menu bar selection if no more popups are left */
2940 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2941 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2943 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2945 /* A sublevel menu was displayed - display the next one
2946 * unless there is another displacement coming up */
2948 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2949 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2950 pmt->hTopMenu, TRUE, wFlags);
2956 /***********************************************************************
2957 * MENU_KeyRight
2959 * Handle a VK_RIGHT key event in a menu.
2961 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2963 HMENU hmenutmp;
2964 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2965 UINT nextcol;
2967 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2968 pmt->hCurrentMenu,
2969 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2970 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2972 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2974 /* If already displaying a popup, try to display sub-popup */
2976 hmenutmp = pmt->hCurrentMenu;
2977 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2979 /* if subpopup was displayed then we are done */
2980 if (hmenutmp != pmt->hCurrentMenu) return;
2983 /* Check to see if there's another column */
2984 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2985 NO_SELECTED_ITEM ) {
2986 TRACE("Going to %d.\n", nextcol );
2987 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2988 nextcol, TRUE, 0 );
2989 return;
2992 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2994 if( pmt->hCurrentMenu != pmt->hTopMenu )
2996 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2997 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2998 } else hmenutmp = 0;
3000 /* try to move to the next item */
3001 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
3002 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
3004 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3005 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
3006 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3007 pmt->hTopMenu, TRUE, wFlags);
3011 static void CALLBACK release_capture( BOOL __normal )
3013 set_capture_window( 0, GUI_INMENUMODE, NULL );
3016 /***********************************************************************
3017 * MENU_TrackMenu
3019 * Menu tracking code.
3021 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
3022 HWND hwnd, const RECT *lprect )
3024 MSG msg;
3025 POPUPMENU *menu;
3026 BOOL fRemove;
3027 INT executedMenuId = -1;
3028 MTRACKER mt;
3029 BOOL enterIdleSent = FALSE;
3030 HWND capture_win;
3032 mt.trackFlags = 0;
3033 mt.hCurrentMenu = hmenu;
3034 mt.hTopMenu = hmenu;
3035 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3036 mt.pt.x = x;
3037 mt.pt.y = y;
3039 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3040 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3042 fEndMenu = FALSE;
3043 if (!(menu = MENU_GetMenu( hmenu )))
3045 WARN("Invalid menu handle %p\n", hmenu);
3046 SetLastError(ERROR_INVALID_MENU_HANDLE);
3047 return FALSE;
3050 if (wFlags & TPM_BUTTONDOWN)
3052 /* Get the result in order to start the tracking or not */
3053 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3054 fEndMenu = !fRemove;
3057 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3059 /* owner may not be visible when tracking a popup, so use the menu itself */
3060 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3061 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3063 __TRY while (!fEndMenu)
3065 menu = MENU_GetMenu( mt.hCurrentMenu );
3066 if (!menu) /* sometimes happens if I do a window manager close */
3067 break;
3069 /* we have to keep the message in the queue until it's
3070 * clear that menu loop is not over yet. */
3072 for (;;)
3074 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3076 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3077 /* remove the message from the queue */
3078 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3080 else
3082 if (!enterIdleSent)
3084 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3085 enterIdleSent = TRUE;
3086 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3088 WaitMessage();
3092 /* check if EndMenu() tried to cancel us, by posting this message */
3093 if(msg.message == WM_CANCELMODE)
3095 /* we are now out of the loop */
3096 fEndMenu = TRUE;
3098 /* remove the message from the queue */
3099 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3101 /* break out of internal loop, ala ESCAPE */
3102 break;
3105 TranslateMessage( &msg );
3106 mt.pt = msg.pt;
3108 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3109 enterIdleSent=FALSE;
3111 fRemove = FALSE;
3112 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3115 * Use the mouse coordinates in lParam instead of those in the MSG
3116 * struct to properly handle synthetic messages. They are already
3117 * in screen coordinates.
3119 mt.pt.x = (short)LOWORD(msg.lParam);
3120 mt.pt.y = (short)HIWORD(msg.lParam);
3122 /* Find a menu for this mouse event */
3123 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3125 switch(msg.message)
3127 /* no WM_NC... messages in captured state */
3129 case WM_RBUTTONDBLCLK:
3130 case WM_RBUTTONDOWN:
3131 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3132 /* fall through */
3133 case WM_LBUTTONDBLCLK:
3134 case WM_LBUTTONDOWN:
3135 /* If the message belongs to the menu, removes it from the queue */
3136 /* Else, end menu tracking */
3137 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3138 fEndMenu = !fRemove;
3139 break;
3141 case WM_RBUTTONUP:
3142 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3143 /* fall through */
3144 case WM_LBUTTONUP:
3145 /* Check if a menu was selected by the mouse */
3146 if (hmenu)
3148 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3149 TRACE("executedMenuId %d\n", executedMenuId);
3151 /* End the loop if executedMenuId is an item ID */
3152 /* or if the job was done (executedMenuId = 0). */
3153 fEndMenu = fRemove = (executedMenuId != -1);
3155 /* No menu was selected by the mouse */
3156 /* if the function was called by TrackPopupMenu, continue
3157 with the menu tracking. If not, stop it */
3158 else
3159 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3161 break;
3163 case WM_MOUSEMOVE:
3164 /* the selected menu item must be changed every time */
3165 /* the mouse moves. */
3167 if (hmenu)
3168 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3170 } /* switch(msg.message) - mouse */
3172 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3174 fRemove = TRUE; /* Keyboard messages are always removed */
3175 switch(msg.message)
3177 case WM_KEYDOWN:
3178 case WM_SYSKEYDOWN:
3179 switch(msg.wParam)
3181 case VK_MENU:
3182 fEndMenu = TRUE;
3183 break;
3185 case VK_HOME:
3186 case VK_END:
3187 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3188 NO_SELECTED_ITEM, FALSE, 0 );
3189 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3190 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3191 break;
3193 case VK_UP:
3194 case VK_DOWN: /* If on menu bar, pull-down the menu */
3196 menu = MENU_GetMenu( mt.hCurrentMenu );
3197 if (!(menu->wFlags & MF_POPUP))
3198 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3199 else /* otherwise try to move selection */
3200 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3201 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3202 break;
3204 case VK_LEFT:
3205 MENU_KeyLeft( &mt, wFlags );
3206 break;
3208 case VK_RIGHT:
3209 MENU_KeyRight( &mt, wFlags );
3210 break;
3212 case VK_ESCAPE:
3213 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3214 break;
3216 case VK_F1:
3218 HELPINFO hi;
3219 hi.cbSize = sizeof(HELPINFO);
3220 hi.iContextType = HELPINFO_MENUITEM;
3221 if (menu->FocusedItem == NO_SELECTED_ITEM)
3222 hi.iCtrlId = 0;
3223 else
3224 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3225 hi.hItemHandle = hmenu;
3226 hi.dwContextId = menu->dwContextHelpID;
3227 hi.MousePos = msg.pt;
3228 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3229 break;
3232 default:
3233 break;
3235 break; /* WM_KEYDOWN */
3237 case WM_CHAR:
3238 case WM_SYSCHAR:
3240 UINT pos;
3242 if (msg.wParam == '\r' || msg.wParam == ' ')
3244 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3245 fEndMenu = (executedMenuId != -2);
3247 break;
3250 /* Hack to avoid control chars. */
3251 /* We will find a better way real soon... */
3252 if (msg.wParam < 32) break;
3254 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3255 LOWORD(msg.wParam), FALSE );
3256 if (pos == (UINT)-2) fEndMenu = TRUE;
3257 else if (pos == (UINT)-1) MessageBeep(0);
3258 else
3260 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3261 TRUE, 0 );
3262 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3263 fEndMenu = (executedMenuId != -2);
3266 break;
3267 } /* switch(msg.message) - kbd */
3269 else
3271 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3272 DispatchMessageW( &msg );
3273 continue;
3276 if (!fEndMenu) fRemove = TRUE;
3278 /* finally remove message from the queue */
3280 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3281 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3282 else mt.trackFlags &= ~TF_SKIPREMOVE;
3284 __FINALLY( release_capture )
3286 /* If dropdown is still painted and the close box is clicked on
3287 then the menu will be destroyed as part of the DispatchMessage above.
3288 This will then invalidate the menu handle in mt.hTopMenu. We should
3289 check for this first. */
3290 if( IsMenu( mt.hTopMenu ) )
3292 menu = MENU_GetMenu( mt.hTopMenu );
3294 if( IsWindow( mt.hOwnerWnd ) )
3296 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3298 if (menu && (menu->wFlags & MF_POPUP))
3300 DestroyWindow( menu->hWnd );
3301 menu->hWnd = 0;
3303 if (!(wFlags & TPM_NONOTIFY))
3304 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3305 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3307 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3308 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3311 /* Reset the variable for hiding menu */
3312 if( menu ) menu->bTimeToHide = FALSE;
3315 /* The return value is only used by TrackPopupMenu */
3316 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3317 if (executedMenuId == -1) executedMenuId = 0;
3318 return executedMenuId;
3321 /***********************************************************************
3322 * MENU_InitTracking
3324 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3326 POPUPMENU *menu;
3328 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3330 HideCaret(0);
3332 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3333 if (!(wFlags & TPM_NONOTIFY))
3334 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3336 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3338 if (!(wFlags & TPM_NONOTIFY))
3340 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3341 /* If an app changed/recreated menu bar entries in WM_INITMENU
3342 * menu sizes will be recalculated once the menu created/shown.
3346 /* This makes the menus of applications built with Delphi work.
3347 * It also enables menus to be displayed in more than one window,
3348 * but there are some bugs left that need to be fixed in this case.
3350 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3352 return TRUE;
3354 /***********************************************************************
3355 * MENU_ExitTracking
3357 static BOOL MENU_ExitTracking(HWND hWnd)
3359 TRACE("hwnd=%p\n", hWnd);
3361 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3362 ShowCaret(0);
3363 top_popup = 0;
3364 return TRUE;
3367 /***********************************************************************
3368 * MENU_TrackMouseMenuBar
3370 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3372 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3374 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3375 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3377 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3379 if (IsMenu(hMenu))
3381 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3382 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3383 MENU_ExitTracking(hWnd);
3388 /***********************************************************************
3389 * MENU_TrackKbdMenuBar
3391 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3393 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3395 UINT uItem = NO_SELECTED_ITEM;
3396 HMENU hTrackMenu;
3397 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3399 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3401 /* find window that has a menu */
3403 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3404 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3406 /* check if we have to track a system menu */
3408 hTrackMenu = GetMenu( hwnd );
3409 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3411 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3412 hTrackMenu = get_win_sys_menu( hwnd );
3413 uItem = 0;
3414 wParam |= HTSYSMENU; /* prevent item lookup */
3417 if (!IsMenu( hTrackMenu )) return;
3419 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3421 if( wChar && wChar != ' ' )
3423 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3424 if ( uItem >= (UINT)(-2) )
3426 if( uItem == (UINT)(-1) ) MessageBeep(0);
3427 /* schedule end of menu tracking */
3428 wFlags |= TF_ENDMENU;
3429 goto track_menu;
3433 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3435 if (!(wParam & HTSYSMENU) || wChar == ' ')
3437 if( uItem == NO_SELECTED_ITEM )
3438 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3439 else
3440 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3443 track_menu:
3444 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3445 MENU_ExitTracking( hwnd );
3449 /**********************************************************************
3450 * TrackPopupMenu (USER32.@)
3452 * Like the win32 API, the function return the command ID only if the
3453 * flag TPM_RETURNCMD is on.
3456 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3457 INT nReserved, HWND hWnd, const RECT *lpRect )
3459 BOOL ret = FALSE;
3461 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3462 hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3464 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3466 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3467 if (!(wFlags & TPM_NONOTIFY))
3468 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3470 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3471 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3472 MENU_ExitTracking(hWnd);
3474 return ret;
3477 /**********************************************************************
3478 * TrackPopupMenuEx (USER32.@)
3480 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3481 HWND hWnd, LPTPMPARAMS lpTpm )
3483 FIXME("not fully implemented\n" );
3484 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3485 lpTpm ? &lpTpm->rcExclude : NULL );
3488 /***********************************************************************
3489 * PopupMenuWndProc
3491 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3493 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3495 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3497 switch(message)
3499 case WM_CREATE:
3501 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3502 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3503 return 0;
3506 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3507 return MA_NOACTIVATE;
3509 case WM_PAINT:
3511 PAINTSTRUCT ps;
3512 BeginPaint( hwnd, &ps );
3513 MENU_DrawPopupMenu( hwnd, ps.hdc,
3514 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3515 EndPaint( hwnd, &ps );
3516 return 0;
3518 case WM_ERASEBKGND:
3519 return 1;
3521 case WM_DESTROY:
3522 /* zero out global pointer in case resident popup window was destroyed. */
3523 if (hwnd == top_popup) top_popup = 0;
3524 break;
3526 case WM_SHOWWINDOW:
3528 if( wParam )
3530 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3532 else
3533 SetWindowLongPtrW( hwnd, 0, 0 );
3534 break;
3536 case MM_SETMENUHANDLE:
3537 SetWindowLongPtrW( hwnd, 0, wParam );
3538 break;
3540 case MM_GETMENUHANDLE:
3541 return GetWindowLongPtrW( hwnd, 0 );
3543 default:
3544 return DefWindowProcW( hwnd, message, wParam, lParam );
3546 return 0;
3550 /***********************************************************************
3551 * MENU_GetMenuBarHeight
3553 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3555 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3556 INT orgX, INT orgY )
3558 HDC hdc;
3559 RECT rectBar;
3560 LPPOPUPMENU lppop;
3562 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3564 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3566 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3567 SelectObject( hdc, get_menu_font(FALSE));
3568 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3569 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3570 ReleaseDC( hwnd, hdc );
3571 return lppop->Height;
3575 /*******************************************************************
3576 * ChangeMenuA (USER32.@)
3578 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3579 UINT id, UINT flags )
3581 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3582 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3583 id, data );
3584 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3585 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3586 id, data );
3587 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3588 flags & MF_BYPOSITION ? pos : id,
3589 flags & ~MF_REMOVE );
3590 /* Default: MF_INSERT */
3591 return InsertMenuA( hMenu, pos, flags, id, data );
3595 /*******************************************************************
3596 * ChangeMenuW (USER32.@)
3598 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3599 UINT id, UINT flags )
3601 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3602 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3603 id, data );
3604 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3605 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3606 id, data );
3607 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3608 flags & MF_BYPOSITION ? pos : id,
3609 flags & ~MF_REMOVE );
3610 /* Default: MF_INSERT */
3611 return InsertMenuW( hMenu, pos, flags, id, data );
3615 /*******************************************************************
3616 * CheckMenuItem (USER32.@)
3618 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3620 MENUITEM *item;
3621 DWORD ret;
3623 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3624 ret = item->fState & MF_CHECKED;
3625 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3626 else item->fState &= ~MF_CHECKED;
3627 return ret;
3631 /**********************************************************************
3632 * EnableMenuItem (USER32.@)
3634 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3636 UINT oldflags;
3637 MENUITEM *item;
3638 POPUPMENU *menu;
3640 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3642 /* Get the Popupmenu to access the owner menu */
3643 if (!(menu = MENU_GetMenu(hMenu)))
3644 return (UINT)-1;
3646 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3647 return (UINT)-1;
3649 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3650 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3652 /* If the close item in the system menu change update the close button */
3653 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3655 if (menu->hSysMenuOwner != 0)
3657 RECT rc;
3658 POPUPMENU* parentMenu;
3660 /* Get the parent menu to access*/
3661 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3662 return (UINT)-1;
3664 /* Refresh the frame to reflect the change */
3665 GetWindowRect(parentMenu->hWnd, &rc);
3666 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3667 rc.bottom = 0;
3668 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3672 return oldflags;
3676 /*******************************************************************
3677 * GetMenuStringA (USER32.@)
3679 INT WINAPI GetMenuStringA(
3680 HMENU hMenu, /* [in] menuhandle */
3681 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3682 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3683 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3684 UINT wFlags /* [in] MF_ flags */
3686 MENUITEM *item;
3688 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3689 if (str && nMaxSiz) str[0] = '\0';
3690 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3691 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3692 return 0;
3694 if (!item->text) return 0;
3695 if (!str || !nMaxSiz) return strlenW(item->text);
3696 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3697 str[nMaxSiz-1] = 0;
3698 TRACE("returning %s\n", debugstr_a(str));
3699 return strlen(str);
3703 /*******************************************************************
3704 * GetMenuStringW (USER32.@)
3706 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3707 LPWSTR str, INT nMaxSiz, UINT wFlags )
3709 MENUITEM *item;
3711 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3712 if (str && nMaxSiz) str[0] = '\0';
3713 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3714 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3715 return 0;
3717 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3718 if( !(item->text)) {
3719 str[0] = 0;
3720 return 0;
3722 lstrcpynW( str, item->text, nMaxSiz );
3723 TRACE("returning %s\n", debugstr_w(str));
3724 return strlenW(str);
3728 /**********************************************************************
3729 * HiliteMenuItem (USER32.@)
3731 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3732 UINT wHilite )
3734 LPPOPUPMENU menu;
3735 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3736 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3737 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3738 if (menu->FocusedItem == wItemID) return TRUE;
3739 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3740 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3741 return TRUE;
3745 /**********************************************************************
3746 * GetMenuState (USER32.@)
3748 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3750 MENUITEM *item;
3751 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3752 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3753 debug_print_menuitem (" item: ", item, "");
3754 if (item->fType & MF_POPUP)
3756 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3757 if (!menu) return -1;
3758 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3760 else
3762 /* We used to (from way back then) mask the result to 0xff. */
3763 /* I don't know why and it seems wrong as the documented */
3764 /* return flag MF_SEPARATOR is outside that mask. */
3765 return (item->fType | item->fState);
3770 /**********************************************************************
3771 * GetMenuItemCount (USER32.@)
3773 INT WINAPI GetMenuItemCount( HMENU hMenu )
3775 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3776 if (!menu) return -1;
3777 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3778 return menu->nItems;
3782 /**********************************************************************
3783 * GetMenuItemID (USER32.@)
3785 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3787 MENUITEM * lpmi;
3789 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3790 if (lpmi->fType & MF_POPUP) return -1;
3791 return lpmi->wID;
3796 /*******************************************************************
3797 * InsertMenuW (USER32.@)
3799 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3800 UINT_PTR id, LPCWSTR str )
3802 MENUITEM *item;
3804 if (IS_STRING_ITEM(flags) && str)
3805 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3806 hMenu, pos, flags, id, debugstr_w(str) );
3807 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3808 hMenu, pos, flags, id, str );
3810 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3812 if (!(MENU_SetItemData( item, flags, id, str )))
3814 RemoveMenu( hMenu, pos, flags );
3815 return FALSE;
3818 item->hCheckBit = item->hUnCheckBit = 0;
3819 return TRUE;
3823 /*******************************************************************
3824 * InsertMenuA (USER32.@)
3826 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3827 UINT_PTR id, LPCSTR str )
3829 BOOL ret = FALSE;
3831 if (IS_STRING_ITEM(flags) && str)
3833 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3834 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3835 if (newstr)
3837 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3838 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3839 HeapFree( GetProcessHeap(), 0, newstr );
3841 return ret;
3843 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3847 /*******************************************************************
3848 * AppendMenuA (USER32.@)
3850 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3851 UINT_PTR id, LPCSTR data )
3853 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3857 /*******************************************************************
3858 * AppendMenuW (USER32.@)
3860 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3861 UINT_PTR id, LPCWSTR data )
3863 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3867 /**********************************************************************
3868 * RemoveMenu (USER32.@)
3870 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3872 LPPOPUPMENU menu;
3873 MENUITEM *item;
3875 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3876 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3877 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3879 /* Remove item */
3881 MENU_FreeItemData( item );
3883 if (--menu->nItems == 0)
3885 HeapFree( GetProcessHeap(), 0, menu->items );
3886 menu->items = NULL;
3888 else
3890 while(nPos < menu->nItems)
3892 *item = *(item+1);
3893 item++;
3894 nPos++;
3896 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3897 menu->nItems * sizeof(MENUITEM) );
3899 return TRUE;
3903 /**********************************************************************
3904 * DeleteMenu (USER32.@)
3906 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3908 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3909 if (!item) return FALSE;
3910 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3911 /* nPos is now the position of the item */
3912 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3913 return TRUE;
3917 /*******************************************************************
3918 * ModifyMenuW (USER32.@)
3920 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3921 UINT_PTR id, LPCWSTR str )
3923 MENUITEM *item;
3925 if (IS_STRING_ITEM(flags))
3926 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3927 else
3928 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3930 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3931 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3932 return MENU_SetItemData( item, flags, id, str );
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 = (LPPOPUPMENU) 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( (LPCVOID)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( (LPCVOID)LoadResource( instance, hrsrc ));
4369 /**********************************************************************
4370 * LoadMenuIndirect (USER.220)
4372 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4374 HMENU hMenu;
4375 WORD version, offset;
4376 LPCSTR p = (LPCSTR)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 = (LPCSTR)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 * SetMenuItemInfo_common
4638 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4639 const MENUITEMINFOW *lpmii,
4640 BOOL unicode)
4642 if (!menu) return FALSE;
4644 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4646 if (lpmii->fMask & MIIM_TYPE ) {
4647 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4648 WARN("invalid combination of fMask bits used\n");
4649 /* this does not happen on Win9x/ME */
4650 SetLastError( ERROR_INVALID_PARAMETER);
4651 return FALSE;
4654 /* Remove the old type bits and replace them with the new ones */
4655 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4656 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4658 if (IS_STRING_ITEM(menu->fType)) {
4659 HeapFree(GetProcessHeap(), 0, menu->text);
4660 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4661 } else if( (menu->fType) & MFT_BITMAP)
4662 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4665 if (lpmii->fMask & MIIM_FTYPE ) {
4666 if(( lpmii->fType & MFT_BITMAP)) {
4667 SetLastError( ERROR_INVALID_PARAMETER);
4668 return FALSE;
4670 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4671 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4673 if (lpmii->fMask & MIIM_STRING ) {
4674 /* free the string when used */
4675 HeapFree(GetProcessHeap(), 0, menu->text);
4676 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4679 if (lpmii->fMask & MIIM_STATE)
4681 /* Other menu items having MFS_DEFAULT are not converted
4682 to normal items */
4683 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4686 if (lpmii->fMask & MIIM_ID)
4687 menu->wID = lpmii->wID;
4689 if (lpmii->fMask & MIIM_SUBMENU) {
4690 menu->hSubMenu = lpmii->hSubMenu;
4691 if (menu->hSubMenu) {
4692 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4693 if (subMenu) {
4694 subMenu->wFlags |= MF_POPUP;
4695 menu->fType |= MF_POPUP;
4697 else {
4698 SetLastError( ERROR_INVALID_PARAMETER);
4699 return FALSE;
4702 else
4703 menu->fType &= ~MF_POPUP;
4706 if (lpmii->fMask & MIIM_CHECKMARKS)
4708 menu->hCheckBit = lpmii->hbmpChecked;
4709 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4711 if (lpmii->fMask & MIIM_DATA)
4712 menu->dwItemData = lpmii->dwItemData;
4714 if (lpmii->fMask & MIIM_BITMAP)
4715 menu->hbmpItem = lpmii->hbmpItem;
4717 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4718 menu->fType |= MFT_SEPARATOR;
4720 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4721 return TRUE;
4724 /**********************************************************************
4725 * SetMenuItemInfoA (USER32.@)
4727 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4728 const MENUITEMINFOA *lpmii)
4730 MENUITEMINFOA mii;
4732 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4734 if( lpmii->cbSize != sizeof( mii) &&
4735 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4736 SetLastError( ERROR_INVALID_PARAMETER);
4737 return FALSE;
4739 memcpy( &mii, lpmii, lpmii->cbSize);
4740 if( lpmii->cbSize != sizeof( mii)) {
4741 mii.cbSize = sizeof( mii);
4742 mii.hbmpItem = NULL;
4744 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4745 (const MENUITEMINFOW *)&mii, FALSE);
4748 /**********************************************************************
4749 * SetMenuItemInfoW (USER32.@)
4751 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4752 const MENUITEMINFOW *lpmii)
4754 MENUITEMINFOW mii;
4756 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4758 if( lpmii->cbSize != sizeof( mii) &&
4759 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4760 SetLastError( ERROR_INVALID_PARAMETER);
4761 return FALSE;
4763 memcpy( &mii, lpmii, lpmii->cbSize);
4764 if( lpmii->cbSize != sizeof( mii)) {
4765 mii.cbSize = sizeof( mii);
4766 mii.hbmpItem = NULL;
4768 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4769 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4772 /**********************************************************************
4773 * SetMenuDefaultItem (USER32.@)
4776 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4778 UINT i;
4779 POPUPMENU *menu;
4780 MENUITEM *item;
4782 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4784 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4786 /* reset all default-item flags */
4787 item = menu->items;
4788 for (i = 0; i < menu->nItems; i++, item++)
4790 item->fState &= ~MFS_DEFAULT;
4793 /* no default item */
4794 if ( -1 == uItem)
4796 return TRUE;
4799 item = menu->items;
4800 if ( bypos )
4802 if ( uItem >= menu->nItems ) return FALSE;
4803 item[uItem].fState |= MFS_DEFAULT;
4804 return TRUE;
4806 else
4808 for (i = 0; i < menu->nItems; i++, item++)
4810 if (item->wID == uItem)
4812 item->fState |= MFS_DEFAULT;
4813 return TRUE;
4818 return FALSE;
4821 /**********************************************************************
4822 * GetMenuDefaultItem (USER32.@)
4824 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4826 POPUPMENU *menu;
4827 MENUITEM * item;
4828 UINT i = 0;
4830 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4832 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4834 /* find default item */
4835 item = menu->items;
4837 /* empty menu */
4838 if (! item) return -1;
4840 while ( !( item->fState & MFS_DEFAULT ) )
4842 i++; item++;
4843 if (i >= menu->nItems ) return -1;
4846 /* default: don't return disabled items */
4847 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4849 /* search rekursiv when needed */
4850 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4852 UINT ret;
4853 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4854 if ( -1 != ret ) return ret;
4856 /* when item not found in submenu, return the popup item */
4858 return ( bypos ) ? i : item->wID;
4863 /**********************************************************************
4864 * InsertMenuItemA (USER32.@)
4866 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4867 const MENUITEMINFOA *lpmii)
4869 MENUITEM *item;
4870 MENUITEMINFOA mii;
4872 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4874 if( lpmii->cbSize != sizeof( mii) &&
4875 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4876 SetLastError( ERROR_INVALID_PARAMETER);
4877 return FALSE;
4879 memcpy( &mii, lpmii, lpmii->cbSize);
4880 if( lpmii->cbSize != sizeof( mii)) {
4881 mii.cbSize = sizeof( mii);
4882 mii.hbmpItem = NULL;
4885 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4886 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4890 /**********************************************************************
4891 * InsertMenuItemW (USER32.@)
4893 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4894 const MENUITEMINFOW *lpmii)
4896 MENUITEM *item;
4897 MENUITEMINFOW mii;
4899 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4901 if( lpmii->cbSize != sizeof( mii) &&
4902 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4903 SetLastError( ERROR_INVALID_PARAMETER);
4904 return FALSE;
4906 memcpy( &mii, lpmii, lpmii->cbSize);
4907 if( lpmii->cbSize != sizeof( mii)) {
4908 mii.cbSize = sizeof( mii);
4909 mii.hbmpItem = NULL;
4912 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4913 return SetMenuItemInfo_common(item, &mii, TRUE);
4916 /**********************************************************************
4917 * CheckMenuRadioItem (USER32.@)
4920 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4921 UINT first, UINT last, UINT check,
4922 UINT bypos)
4924 BOOL done = FALSE;
4925 UINT i;
4926 MENUITEM *mi_first = NULL, *mi_check;
4927 HMENU m_first, m_check;
4929 for (i = first; i <= last; i++)
4931 UINT pos = i;
4933 if (!mi_first)
4935 m_first = hMenu;
4936 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4937 if (!mi_first) continue;
4938 mi_check = mi_first;
4939 m_check = m_first;
4941 else
4943 m_check = hMenu;
4944 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4945 if (!mi_check) continue;
4948 if (m_first != m_check) continue;
4949 if (mi_check->fType == MFT_SEPARATOR) continue;
4951 if (i == check)
4953 mi_check->fType |= MFT_RADIOCHECK;
4954 mi_check->fState |= MFS_CHECKED;
4955 done = TRUE;
4957 else
4959 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4960 mi_check->fState &= ~MFS_CHECKED;
4964 return done;
4968 /**********************************************************************
4969 * GetMenuItemRect (USER32.@)
4971 * ATTENTION: Here, the returned values in rect are the screen
4972 * coordinates of the item just like if the menu was
4973 * always on the upper left side of the application.
4976 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4977 LPRECT rect)
4979 POPUPMENU *itemMenu;
4980 MENUITEM *item;
4981 HWND referenceHwnd;
4983 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4985 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4986 referenceHwnd = hwnd;
4988 if(!hwnd)
4990 itemMenu = MENU_GetMenu(hMenu);
4991 if (itemMenu == NULL)
4992 return FALSE;
4994 if(itemMenu->hWnd == 0)
4995 return FALSE;
4996 referenceHwnd = itemMenu->hWnd;
4999 if ((rect == NULL) || (item == NULL))
5000 return FALSE;
5002 *rect = item->rect;
5004 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5006 return TRUE;
5010 /**********************************************************************
5011 * SetMenuInfo (USER32.@)
5013 * FIXME
5014 * MIM_APPLYTOSUBMENUS
5015 * actually use the items to draw the menu
5017 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5019 POPUPMENU *menu;
5021 TRACE("(%p %p)\n", hMenu, lpmi);
5023 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5026 if (lpmi->fMask & MIM_BACKGROUND)
5027 menu->hbrBack = lpmi->hbrBack;
5029 if (lpmi->fMask & MIM_HELPID)
5030 menu->dwContextHelpID = lpmi->dwContextHelpID;
5032 if (lpmi->fMask & MIM_MAXHEIGHT)
5033 menu->cyMax = lpmi->cyMax;
5035 if (lpmi->fMask & MIM_MENUDATA)
5036 menu->dwMenuData = lpmi->dwMenuData;
5038 if (lpmi->fMask & MIM_STYLE)
5040 menu->dwStyle = lpmi->dwStyle;
5041 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5042 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5043 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5046 return TRUE;
5048 return FALSE;
5051 /**********************************************************************
5052 * GetMenuInfo (USER32.@)
5054 * NOTES
5055 * win98/NT5.0
5058 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5059 { POPUPMENU *menu;
5061 TRACE("(%p %p)\n", hMenu, lpmi);
5063 if (lpmi && (menu = MENU_GetMenu(hMenu)))
5066 if (lpmi->fMask & MIM_BACKGROUND)
5067 lpmi->hbrBack = menu->hbrBack;
5069 if (lpmi->fMask & MIM_HELPID)
5070 lpmi->dwContextHelpID = menu->dwContextHelpID;
5072 if (lpmi->fMask & MIM_MAXHEIGHT)
5073 lpmi->cyMax = menu->cyMax;
5075 if (lpmi->fMask & MIM_MENUDATA)
5076 lpmi->dwMenuData = menu->dwMenuData;
5078 if (lpmi->fMask & MIM_STYLE)
5079 lpmi->dwStyle = menu->dwStyle;
5081 return TRUE;
5083 return FALSE;
5087 /**********************************************************************
5088 * SetMenuContextHelpId (USER32.@)
5090 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5092 LPPOPUPMENU menu;
5094 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5096 if ((menu = MENU_GetMenu(hMenu)))
5098 menu->dwContextHelpID = dwContextHelpID;
5099 return TRUE;
5101 return FALSE;
5105 /**********************************************************************
5106 * GetMenuContextHelpId (USER32.@)
5108 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5110 LPPOPUPMENU menu;
5112 TRACE("(%p)\n", hMenu);
5114 if ((menu = MENU_GetMenu(hMenu)))
5116 return menu->dwContextHelpID;
5118 return 0;
5121 /**********************************************************************
5122 * MenuItemFromPoint (USER32.@)
5124 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5126 POPUPMENU *menu = MENU_GetMenu(hMenu);
5127 UINT pos;
5129 /*FIXME: Do we have to handle hWnd here? */
5130 if (!menu) return -1;
5131 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5132 return pos;
5136 /**********************************************************************
5137 * translate_accelerator
5139 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5140 BYTE fVirt, WORD key, WORD cmd )
5142 INT mask = 0;
5143 UINT mesg = 0;
5145 if (wParam != key) return FALSE;
5147 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5148 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5149 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5151 if (message == WM_CHAR || message == WM_SYSCHAR)
5153 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5155 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5156 goto found;
5159 else
5161 if(fVirt & FVIRTKEY)
5163 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5164 wParam, 0xff & HIWORD(lParam));
5166 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5167 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5169 else
5171 if (!(lParam & 0x01000000)) /* no special_key */
5173 if ((fVirt & FALT) && (lParam & 0x20000000))
5174 { /* ^^ ALT pressed */
5175 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5176 goto found;
5181 return FALSE;
5183 found:
5184 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5185 mesg = 1;
5186 else
5188 HMENU hMenu, hSubMenu, hSysMenu;
5189 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5191 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5192 hSysMenu = get_win_sys_menu( hWnd );
5194 /* find menu item and ask application to initialize it */
5195 /* 1. in the system menu */
5196 hSubMenu = hSysMenu;
5197 nPos = cmd;
5198 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5200 if (GetCapture())
5201 mesg = 2;
5202 if (!IsWindowEnabled(hWnd))
5203 mesg = 3;
5204 else
5206 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5207 if(hSubMenu != hSysMenu)
5209 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5210 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5211 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5213 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5216 else /* 2. in the window's menu */
5218 hSubMenu = hMenu;
5219 nPos = cmd;
5220 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5222 if (GetCapture())
5223 mesg = 2;
5224 if (!IsWindowEnabled(hWnd))
5225 mesg = 3;
5226 else
5228 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5229 if(hSubMenu != hMenu)
5231 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5232 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5233 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5235 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5240 if (mesg == 0)
5242 if (uSysStat != (UINT)-1)
5244 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5245 mesg=4;
5246 else
5247 mesg=WM_SYSCOMMAND;
5249 else
5251 if (uStat != (UINT)-1)
5253 if (IsIconic(hWnd))
5254 mesg=5;
5255 else
5257 if (uStat & (MF_DISABLED|MF_GRAYED))
5258 mesg=6;
5259 else
5260 mesg=WM_COMMAND;
5263 else
5264 mesg=WM_COMMAND;
5269 if( mesg==WM_COMMAND )
5271 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5272 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5274 else if( mesg==WM_SYSCOMMAND )
5276 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5277 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5279 else
5281 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5282 * #0: unknown (please report!)
5283 * #1: for WM_KEYUP,WM_SYSKEYUP
5284 * #2: mouse is captured
5285 * #3: window is disabled
5286 * #4: it's a disabled system menu option
5287 * #5: it's a menu option, but window is iconic
5288 * #6: it's a menu option, but disabled
5290 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5291 if(mesg==0)
5292 ERR_(accel)(" unknown reason - please report!\n");
5294 return TRUE;
5297 /**********************************************************************
5298 * TranslateAcceleratorA (USER32.@)
5299 * TranslateAccelerator (USER32.@)
5301 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5303 /* YES, Accel16! */
5304 LPACCEL16 lpAccelTbl;
5305 int i;
5306 WPARAM wParam;
5308 if (!hWnd || !msg) return 0;
5310 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5312 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5313 return 0;
5316 wParam = msg->wParam;
5318 switch (msg->message)
5320 case WM_KEYDOWN:
5321 case WM_SYSKEYDOWN:
5322 break;
5324 case WM_CHAR:
5325 case WM_SYSCHAR:
5327 char ch = LOWORD(wParam);
5328 WCHAR wch;
5329 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5330 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5332 break;
5334 default:
5335 return 0;
5338 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5339 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5340 i = 0;
5343 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5344 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5345 return 1;
5346 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5348 return 0;
5351 /**********************************************************************
5352 * TranslateAcceleratorW (USER32.@)
5354 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5356 /* YES, Accel16! */
5357 LPACCEL16 lpAccelTbl;
5358 int i;
5360 if (!hWnd || !msg) return 0;
5362 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5364 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5365 return 0;
5368 switch (msg->message)
5370 case WM_KEYDOWN:
5371 case WM_SYSKEYDOWN:
5372 case WM_CHAR:
5373 case WM_SYSCHAR:
5374 break;
5376 default:
5377 return 0;
5380 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5381 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5382 i = 0;
5385 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5386 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5387 return 1;
5388 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5390 return 0;