Zero out return string in GetMenuStringA/W.
[wine/wine-kai.git] / dlls / user / menu.c
blob48c490926cedb5f52787630d61562120f8140b66
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Note: the style MF_MOUSESELECT is used to mark popup items that
26 * have been selected, i.e. their popup menu is currently displayed.
27 * This is probably not the meaning this style has in MS-Windows.
29 * TODO:
30 * implements styles :
31 * - MNS_AUTODISMISS
32 * - MNS_DRAGDROP
33 * - MNS_MODELESS
34 * - MNS_NOCHECK
35 * - MNS_NOTIFYBYPOS
38 #include "config.h"
39 #include "wine/port.h"
41 #include <stdarg.h>
42 #include <string.h>
44 #include "windef.h"
45 #include "winbase.h"
46 #include "wingdi.h"
47 #include "winnls.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
50 #include "wownt32.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
61 /* internal popup menu window messages */
63 #define MM_SETMENUHANDLE (WM_USER + 0)
64 #define MM_GETMENUHANDLE (WM_USER + 1)
66 /* Menu item structure */
67 typedef struct {
68 /* ----------- MENUITEMINFO Stuff ----------- */
69 UINT fType; /* Item type. */
70 UINT fState; /* Item state. */
71 UINT_PTR wID; /* Item id. */
72 HMENU hSubMenu; /* Pop-up menu. */
73 HBITMAP hCheckBit; /* Bitmap when checked. */
74 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
75 LPWSTR text; /* Item text or bitmap handle. */
76 DWORD dwItemData; /* Application defined. */
77 DWORD dwTypeData; /* depends on fMask */
78 HBITMAP hbmpItem; /* bitmap in win98 style menus */
79 /* ----------- Wine stuff ----------- */
80 RECT rect; /* Item area (relative to menu window) */
81 UINT xTab; /* X position of text after Tab */
82 } MENUITEM;
84 /* Popup menu structure */
85 typedef struct {
86 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
87 WORD wMagic; /* Magic number */
88 WORD Width; /* Width of the whole menu */
89 WORD Height; /* Height of the whole menu */
90 UINT nItems; /* Number of items in the menu */
91 HWND hWnd; /* Window containing the menu */
92 MENUITEM *items; /* Array of menu items */
93 UINT FocusedItem; /* Currently focused item */
94 HWND hwndOwner; /* window receiving the messages for ownerdraw */
95 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
96 /* ------------ MENUINFO members ------ */
97 DWORD dwStyle; /* Extended mennu style */
98 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
99 HBRUSH hbrBack; /* brush for menu background */
100 DWORD dwContextHelpID;
101 DWORD dwMenuData; /* application defined value */
102 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
103 SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
106 /* internal flags for menu tracking */
108 #define TF_ENDMENU 0x0001
109 #define TF_SUSPENDPOPUP 0x0002
110 #define TF_SKIPREMOVE 0x0004
112 typedef struct
114 UINT trackFlags;
115 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116 HMENU hTopMenu; /* initial menu */
117 HWND hOwnerWnd; /* where notifications are sent */
118 POINT pt;
119 } MTRACKER;
121 #define MENU_MAGIC 0x554d /* 'MU' */
123 #define ITEM_PREV -1
124 #define ITEM_NEXT 1
126 /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL 0xF0000000
128 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
130 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
132 /* popup menu shade thickness */
133 #define POPUP_XSHADE 4
134 #define POPUP_YSHADE 4
136 /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
139 /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
142 /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
145 /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM 0xffff
148 #define MENU_ITEM_TYPE(flags) \
149 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
151 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
152 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
153 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
155 #define IS_SYSTEM_MENU(menu) \
156 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
158 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
159 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
160 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
161 MF_POPUP | MF_SYSMENU | MF_HELP)
162 #define STATE_MASK (~TYPE_MASK)
164 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
166 /* Dimension of the menu bitmaps */
167 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
169 static HBITMAP hStdMnArrow = 0;
170 static HBITMAP hBmpSysMenu = 0;
172 static HBRUSH hShadeBrush = 0;
173 static HFONT hMenuFont = 0;
174 static HFONT hMenuFontBold = 0;
176 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
182 /* Flag set by EndMenu() to force an exit from menu tracking */
183 static BOOL fEndMenu = FALSE;
185 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
187 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
189 /*********************************************************************
190 * menu class descriptor
192 const struct builtin_class_descr MENU_builtin_class =
194 POPUPMENU_CLASS_ATOMA, /* name */
195 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
196 NULL, /* procA (winproc is Unicode only) */
197 PopupMenuWndProc, /* procW */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
213 #define MENUOUT(text) \
214 DPRINTF("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
221 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
222 const char *postfix)
224 TRACE("%s ", prefix);
225 if (mp) {
226 UINT flags = mp->fType;
227 int type = MENU_ITEM_TYPE(flags);
228 DPRINTF( "{ ID=0x%x", mp->wID);
229 if (flags & MF_POPUP)
230 DPRINTF( ", Sub=%p", mp->hSubMenu);
231 if (flags) {
232 int count = 0;
233 DPRINTF( ", Type=");
234 if (type == MFT_STRING)
235 /* Nothing */ ;
236 else if (type == MFT_SEPARATOR)
237 MENUOUT("sep");
238 else if (type == MFT_OWNERDRAW)
239 MENUOUT("own");
240 else if (type == MFT_BITMAP)
241 MENUOUT("bit");
242 else
243 MENUOUT("???");
244 flags -= type;
246 MENUFLAG(MF_POPUP, "pop");
247 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
248 MENUFLAG(MFT_MENUBREAK, "brk");
249 MENUFLAG(MFT_RADIOCHECK, "radio");
250 MENUFLAG(MFT_RIGHTORDER, "rorder");
251 MENUFLAG(MF_SYSMENU, "sys");
252 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
254 if (flags)
255 DPRINTF( "+0x%x", flags);
257 flags = mp->fState;
258 if (flags) {
259 int count = 0;
260 DPRINTF( ", State=");
261 MENUFLAG(MFS_GRAYED, "grey");
262 MENUFLAG(MFS_DEFAULT, "default");
263 MENUFLAG(MFS_DISABLED, "dis");
264 MENUFLAG(MFS_CHECKED, "check");
265 MENUFLAG(MFS_HILITE, "hi");
266 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
267 MENUFLAG(MF_MOUSESELECT, "mouse");
268 if (flags)
269 DPRINTF( "+0x%x", flags);
271 if (mp->hCheckBit)
272 DPRINTF( ", Chk=%p", mp->hCheckBit);
273 if (mp->hUnCheckBit)
274 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
276 if (type == MFT_STRING) {
277 if (mp->text)
278 DPRINTF( ", Text=%s", debugstr_w(mp->text));
279 else
280 DPRINTF( ", Text=Null");
281 } else if (mp->text == NULL)
282 /* Nothing */ ;
283 else
284 DPRINTF( ", Text=%p", mp->text);
285 if (mp->dwItemData)
286 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
287 DPRINTF( " }");
288 } else {
289 DPRINTF( "NULL");
292 DPRINTF(" %s\n", postfix);
295 #undef MENUOUT
296 #undef MENUFLAG
299 /***********************************************************************
300 * MENU_GetMenu
302 * Validate the given menu handle and returns the menu structure pointer.
304 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
306 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
307 if (!menu || menu->wMagic != MENU_MAGIC)
309 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
310 menu = NULL;
312 return menu;
315 /***********************************************************************
316 * get_win_sys_menu
318 * Get the system menu of a window
320 static HMENU get_win_sys_menu( HWND hwnd )
322 HMENU ret = 0;
323 WND *win = WIN_GetPtr( hwnd );
324 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
326 ret = win->hSysMenu;
327 WIN_ReleasePtr( win );
329 return ret;
332 /***********************************************************************
333 * MENU_CopySysPopup
335 * Return the default system menu.
337 static HMENU MENU_CopySysPopup(void)
339 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
340 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
342 if( hMenu ) {
343 POPUPMENU* menu = MENU_GetMenu(hMenu);
344 menu->wFlags |= MF_SYSMENU | MF_POPUP;
345 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
347 else
348 ERR("Unable to load default system menu\n" );
350 TRACE("returning %p.\n", hMenu );
352 return hMenu;
356 /**********************************************************************
357 * MENU_GetSysMenu
359 * Create a copy of the system menu. System menu in Windows is
360 * a special menu bar with the single entry - system menu popup.
361 * This popup is presented to the outside world as a "system menu".
362 * However, the real system menu handle is sometimes seen in the
363 * WM_MENUSELECT parameters (and Word 6 likes it this way).
365 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
367 HMENU hMenu;
369 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
370 if ((hMenu = CreateMenu()))
372 POPUPMENU *menu = MENU_GetMenu(hMenu);
373 menu->wFlags = MF_SYSMENU;
374 menu->hWnd = WIN_GetFullHandle( hWnd );
375 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
377 if (hPopupMenu == (HMENU)(-1))
378 hPopupMenu = MENU_CopySysPopup();
379 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
381 if (hPopupMenu)
383 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
384 (UINT_PTR)hPopupMenu, NULL );
386 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
387 menu->items[0].fState = 0;
388 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
390 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
391 return hMenu;
393 DestroyMenu( hMenu );
395 ERR("failed to load system menu!\n");
396 return 0;
400 /***********************************************************************
401 * MENU_Init
403 * Menus initialisation.
405 BOOL MENU_Init(void)
407 HBITMAP hBitmap;
408 NONCLIENTMETRICSW ncm;
410 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
411 0x55, 0, 0xAA, 0,
412 0x55, 0, 0xAA, 0,
413 0x55, 0, 0xAA, 0 };
415 /* Load menu bitmaps */
416 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
417 /* Load system buttons bitmaps */
418 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
420 if (hStdMnArrow)
422 BITMAP bm;
423 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
424 arrow_bitmap_width = bm.bmWidth;
425 arrow_bitmap_height = bm.bmHeight;
426 } else
427 return FALSE;
429 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
430 return FALSE;
432 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
433 return FALSE;
435 DeleteObject( hBitmap );
436 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
437 return FALSE;
439 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
440 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
441 return FALSE;
443 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
444 return FALSE;
446 ncm.lfMenuFont.lfWeight += 300;
447 if ( ncm.lfMenuFont.lfWeight > 1000)
448 ncm.lfMenuFont.lfWeight = 1000;
450 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
451 return FALSE;
453 return TRUE;
456 /***********************************************************************
457 * MENU_InitSysMenuPopup
459 * Grey the appropriate items in System menu.
461 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
463 BOOL gray;
465 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
466 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
467 gray = ((style & WS_MAXIMIZE) != 0);
468 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
469 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
470 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
471 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
472 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
473 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
474 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
475 gray = (clsStyle & CS_NOCLOSE) != 0;
477 /* The menu item must keep its state if it's disabled */
478 if(gray)
479 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
483 /******************************************************************************
485 * UINT MENU_GetStartOfNextColumn(
486 * HMENU hMenu )
488 *****************************************************************************/
490 static UINT MENU_GetStartOfNextColumn(
491 HMENU hMenu )
493 POPUPMENU *menu = MENU_GetMenu(hMenu);
494 UINT i;
496 if(!menu)
497 return NO_SELECTED_ITEM;
499 i = menu->FocusedItem + 1;
500 if( i == NO_SELECTED_ITEM )
501 return i;
503 for( ; i < menu->nItems; ++i ) {
504 if (menu->items[i].fType & MF_MENUBARBREAK)
505 return i;
508 return NO_SELECTED_ITEM;
512 /******************************************************************************
514 * UINT MENU_GetStartOfPrevColumn(
515 * HMENU hMenu )
517 *****************************************************************************/
519 static UINT MENU_GetStartOfPrevColumn(
520 HMENU hMenu )
522 POPUPMENU *menu = MENU_GetMenu(hMenu);
523 UINT i;
525 if( !menu )
526 return NO_SELECTED_ITEM;
528 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
529 return NO_SELECTED_ITEM;
531 /* Find the start of the column */
533 for(i = menu->FocusedItem; i != 0 &&
534 !(menu->items[i].fType & MF_MENUBARBREAK);
535 --i); /* empty */
537 if(i == 0)
538 return NO_SELECTED_ITEM;
540 for(--i; i != 0; --i) {
541 if (menu->items[i].fType & MF_MENUBARBREAK)
542 break;
545 TRACE("ret %d.\n", i );
547 return i;
552 /***********************************************************************
553 * MENU_FindItem
555 * Find a menu item. Return a pointer on the item, and modifies *hmenu
556 * in case the item was in a sub-menu.
558 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
560 POPUPMENU *menu;
561 UINT i;
563 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
564 if (wFlags & MF_BYPOSITION)
566 if (*nPos >= menu->nItems) return NULL;
567 return &menu->items[*nPos];
569 else
571 MENUITEM *item = menu->items;
572 for (i = 0; i < menu->nItems; i++, item++)
574 if (item->wID == *nPos)
576 *nPos = i;
577 return item;
579 else if (item->fType & MF_POPUP)
581 HMENU hsubmenu = item->hSubMenu;
582 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
583 if (subitem)
585 *hmenu = hsubmenu;
586 return subitem;
591 return NULL;
594 /***********************************************************************
595 * MENU_FindSubMenu
597 * Find a Sub menu. Return the position of the submenu, and modifies
598 * *hmenu in case it is found in another sub-menu.
599 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
601 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
603 POPUPMENU *menu;
604 UINT i;
605 MENUITEM *item;
606 if (((*hmenu)==(HMENU)0xffff) ||
607 (!(menu = MENU_GetMenu(*hmenu))))
608 return NO_SELECTED_ITEM;
609 item = menu->items;
610 for (i = 0; i < menu->nItems; i++, item++) {
611 if(!(item->fType & MF_POPUP)) continue;
612 if (item->hSubMenu == hSubTarget) {
613 return i;
615 else {
616 HMENU hsubmenu = item->hSubMenu;
617 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
618 if (pos != NO_SELECTED_ITEM) {
619 *hmenu = hsubmenu;
620 return pos;
624 return NO_SELECTED_ITEM;
627 /***********************************************************************
628 * MENU_FreeItemData
630 static void MENU_FreeItemData( MENUITEM* item )
632 /* delete text */
633 if (IS_STRING_ITEM(item->fType) && item->text)
634 HeapFree( GetProcessHeap(), 0, item->text );
637 /***********************************************************************
638 * MENU_FindItemByCoords
640 * Find the item at the specified coordinates (screen coords). Does
641 * not work for child windows and therefore should not be called for
642 * an arbitrary system menu.
644 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
645 POINT pt, UINT *pos )
647 MENUITEM *item;
648 UINT i;
649 RECT wrect;
651 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
652 pt.x -= wrect.left;pt.y -= wrect.top;
653 item = menu->items;
654 for (i = 0; i < menu->nItems; i++, item++)
656 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
657 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
659 if (pos) *pos = i;
660 return item;
663 return NULL;
667 /***********************************************************************
668 * MENU_FindItemByKey
670 * Find the menu item selected by a key press.
671 * Return item id, -1 if none, -2 if we should close the menu.
673 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
674 WCHAR key, BOOL forceMenuChar )
676 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
678 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
680 if (hmenu)
682 POPUPMENU *menu = MENU_GetMenu( hmenu );
683 MENUITEM *item = menu->items;
684 LRESULT menuchar;
686 if( !forceMenuChar )
688 UINT i;
690 for (i = 0; i < menu->nItems; i++, item++)
692 if (IS_STRING_ITEM(item->fType) && item->text)
694 WCHAR *p = item->text - 2;
697 p = strchrW (p + 2, '&');
699 while (p != NULL && p [1] == '&');
700 if (p && (toupperW(p[1]) == toupperW(key))) return i;
704 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
705 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
706 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
707 if (HIWORD(menuchar) == 1) return (UINT)(-2);
709 return (UINT)(-1);
713 /***********************************************************************
714 * MENU_GetBitmapItemSize
716 * Get the size of a bitmap item.
718 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
720 BITMAP bm;
721 HBITMAP bmp = (HBITMAP)id;
723 size->cx = size->cy = 0;
725 /* check if there is a magic menu item associated with this item */
726 if (id && IS_MAGIC_ITEM( id ))
728 switch(LOWORD(id))
730 case (INT_PTR)HBMMENU_SYSTEM:
731 if (data)
733 bmp = (HBITMAP)data;
734 break;
736 /* fall through */
737 case (INT_PTR)HBMMENU_MBAR_RESTORE:
738 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
739 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
740 case (INT_PTR)HBMMENU_MBAR_CLOSE:
741 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
742 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
743 size->cy = size->cx;
744 return;
745 case (INT_PTR)HBMMENU_CALLBACK:
746 case (INT_PTR)HBMMENU_POPUP_CLOSE:
747 case (INT_PTR)HBMMENU_POPUP_RESTORE:
748 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
749 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
750 default:
751 FIXME("Magic 0x%08x not implemented\n", id);
752 return;
755 if (GetObjectW(bmp, sizeof(bm), &bm ))
757 size->cx = bm.bmWidth;
758 size->cy = bm.bmHeight;
762 /***********************************************************************
763 * MENU_DrawBitmapItem
765 * Draw a bitmap item.
766 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
768 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
770 BITMAP bm;
771 DWORD rop;
772 HDC hdcMem;
773 HBITMAP bmp = (HBITMAP)lpitem->text;
774 int w = rect->right - rect->left;
775 int h = rect->bottom - rect->top;
776 int bmp_xoffset = 0;
777 int left, top;
778 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
780 /* Check if there is a magic menu item associated with this item */
781 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
783 UINT flags = 0;
784 RECT r;
786 switch(LOWORD(hbmToDraw))
788 case (INT_PTR)HBMMENU_SYSTEM:
789 if (lpitem->dwItemData)
791 bmp = (HBITMAP)lpitem->dwItemData;
792 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
794 else
796 bmp = hBmpSysMenu;
797 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
798 /* only use right half of the bitmap */
799 bmp_xoffset = bm.bmWidth / 2;
800 bm.bmWidth -= bmp_xoffset;
802 goto got_bitmap;
803 case (INT_PTR)HBMMENU_MBAR_RESTORE:
804 flags = DFCS_CAPTIONRESTORE;
805 break;
806 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
807 flags = DFCS_CAPTIONMIN;
808 break;
809 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
810 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
811 break;
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 flags = DFCS_CAPTIONCLOSE;
814 break;
815 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
816 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
817 break;
818 case (INT_PTR)HBMMENU_CALLBACK:
819 case (INT_PTR)HBMMENU_POPUP_CLOSE:
820 case (INT_PTR)HBMMENU_POPUP_RESTORE:
821 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
822 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
823 default:
824 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
825 return;
827 r = *rect;
828 InflateRect( &r, -1, -1 );
829 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
830 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
831 return;
834 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
836 got_bitmap:
837 hdcMem = CreateCompatibleDC( hdc );
838 SelectObject( hdcMem, bmp );
840 /* handle fontsize > bitmap_height */
841 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
842 left=rect->left;
843 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
844 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
845 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
846 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
847 DeleteDC( hdcMem );
851 /***********************************************************************
852 * MENU_CalcItemSize
854 * Calculate the size of the menu item and store it in lpitem->rect.
856 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
857 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
859 WCHAR *p;
860 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
862 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
863 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
864 (menuBar ? " (MenuBar)" : ""));
866 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
868 if (lpitem->fType & MF_OWNERDRAW)
871 ** Experimentation under Windows reveals that an owner-drawn
872 ** menu is expected to return the size of the content part of
873 ** the menu item, not including the checkmark nor the submenu
874 ** arrow. Windows adds those values itself and returns the
875 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
877 MEASUREITEMSTRUCT mis;
878 mis.CtlType = ODT_MENU;
879 mis.CtlID = 0;
880 mis.itemID = lpitem->wID;
881 mis.itemData = (DWORD)lpitem->dwItemData;
882 mis.itemHeight = 0;
883 mis.itemWidth = 0;
884 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
885 lpitem->rect.right += mis.itemWidth;
887 if (menuBar)
889 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
892 /* under at least win95 you seem to be given a standard
893 height for the menu and the height value is ignored */
894 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
896 else
897 lpitem->rect.bottom += mis.itemHeight;
899 TRACE("id=%04x size=%dx%d\n",
900 lpitem->wID, mis.itemWidth, mis.itemHeight);
901 /* Fall through to get check/arrow width calculation. */
904 if (lpitem->fType & MF_SEPARATOR)
906 lpitem->rect.bottom += SEPARATOR_HEIGHT;
907 return;
910 if (!menuBar)
912 /* New style MIIM_BITMAP */
913 if (lpitem->hbmpItem)
915 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
917 MEASUREITEMSTRUCT measItem;
918 measItem.CtlType = ODT_MENU;
919 measItem.CtlID = 0;
920 measItem.itemID = lpitem->wID;
921 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
922 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
923 measItem.itemData = lpitem->dwItemData;
925 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
927 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
928 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
929 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
930 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
931 } else {
932 SIZE size;
933 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
934 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
935 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
936 lpitem->rect.right += size.cx;
937 lpitem->rect.bottom += size.cy;
939 if (lppop->dwStyle & MNS_CHECKORBMP)
940 lpitem->rect.right += check_bitmap_width;
941 else
942 lpitem->rect.right += 2 * check_bitmap_width;
943 } else
944 lpitem->rect.right += 2 * check_bitmap_width;
945 if (lpitem->fType & MF_POPUP)
946 lpitem->rect.right += arrow_bitmap_width;
949 if (lpitem->fType & MF_OWNERDRAW)
950 return;
952 if (IS_BITMAP_ITEM(lpitem->fType))
954 SIZE size;
956 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
957 lpitem->rect.right += size.cx;
958 lpitem->rect.bottom += size.cy;
959 /* Leave space for the sunken border */
960 lpitem->rect.right += 2;
961 lpitem->rect.bottom += 2;
965 /* it must be a text item - unless it's the system menu */
966 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
967 { SIZE size;
969 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
971 lpitem->rect.right += size.cx;
972 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
973 lpitem->xTab = 0;
975 if (menuBar)
977 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
979 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
981 /* Item contains a tab (only meaningful in popup menus) */
982 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
983 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
984 lpitem->rect.right += MENU_TAB_SPACE;
986 else
988 if (strchrW( lpitem->text, '\b' ))
989 lpitem->rect.right += MENU_TAB_SPACE;
990 lpitem->xTab = lpitem->rect.right - check_bitmap_width
991 - arrow_bitmap_width;
994 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
998 /***********************************************************************
999 * MENU_PopupMenuCalcSize
1001 * Calculate the size of a popup menu.
1003 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1005 MENUITEM *lpitem;
1006 HDC hdc;
1007 int start, i;
1008 int orgX, orgY, maxX, maxTab, maxTabWidth;
1010 lppop->Width = lppop->Height = 0;
1011 if (lppop->nItems == 0) return;
1012 hdc = GetDC( 0 );
1014 SelectObject( hdc, hMenuFont);
1016 start = 0;
1017 maxX = 2 + 1;
1019 lppop->maxBmpSize.cx = 0;
1020 lppop->maxBmpSize.cy = 0;
1022 while (start < lppop->nItems)
1024 lpitem = &lppop->items[start];
1025 orgX = maxX;
1026 orgY = 3;
1028 maxTab = maxTabWidth = 0;
1029 /* Parse items until column break or end of menu */
1030 for (i = start; i < lppop->nItems; i++, lpitem++)
1032 if ((i != start) &&
1033 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1035 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1037 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1038 maxX = max( maxX, lpitem->rect.right );
1039 orgY = lpitem->rect.bottom;
1040 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1042 maxTab = max( maxTab, lpitem->xTab );
1043 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1047 /* Finish the column (set all items to the largest width found) */
1048 maxX = max( maxX, maxTab + maxTabWidth );
1049 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1051 lpitem->rect.right = maxX;
1052 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1053 lpitem->xTab = maxTab;
1056 lppop->Height = max( lppop->Height, orgY );
1059 lppop->Width = maxX;
1061 /* space for 3d border */
1062 lppop->Height += 2;
1063 lppop->Width += 2;
1065 ReleaseDC( 0, hdc );
1069 /***********************************************************************
1070 * MENU_MenuBarCalcSize
1072 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1073 * height is off by 1 pixel which causes lengthy window relocations when
1074 * active document window is maximized/restored.
1076 * Calculate the size of the menu bar.
1078 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1079 LPPOPUPMENU lppop, HWND hwndOwner )
1081 MENUITEM *lpitem;
1082 int start, i, orgX, orgY, maxY, helpPos;
1084 if ((lprect == NULL) || (lppop == NULL)) return;
1085 if (lppop->nItems == 0) return;
1086 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1087 lprect->left, lprect->top, lprect->right, lprect->bottom);
1088 lppop->Width = lprect->right - lprect->left;
1089 lppop->Height = 0;
1090 maxY = lprect->top+1;
1091 start = 0;
1092 helpPos = -1;
1093 lppop->maxBmpSize.cx = 0;
1094 lppop->maxBmpSize.cy = 0;
1095 while (start < lppop->nItems)
1097 lpitem = &lppop->items[start];
1098 orgX = lprect->left;
1099 orgY = maxY;
1101 /* Parse items until line break or end of menu */
1102 for (i = start; i < lppop->nItems; i++, lpitem++)
1104 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1105 if ((i != start) &&
1106 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1108 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1109 orgX, orgY );
1110 debug_print_menuitem (" item: ", lpitem, "");
1111 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1113 if (lpitem->rect.right > lprect->right)
1115 if (i != start) break;
1116 else lpitem->rect.right = lprect->right;
1118 maxY = max( maxY, lpitem->rect.bottom );
1119 orgX = lpitem->rect.right;
1122 /* Finish the line (set all items to the largest height found) */
1123 while (start < i) lppop->items[start++].rect.bottom = maxY;
1126 lprect->bottom = maxY;
1127 lppop->Height = lprect->bottom - lprect->top;
1129 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1130 /* the last item (if several lines, only move the last line) */
1131 lpitem = &lppop->items[lppop->nItems-1];
1132 orgY = lpitem->rect.top;
1133 orgX = lprect->right;
1134 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1135 if ( (helpPos==-1) || (helpPos>i) )
1136 break; /* done */
1137 if (lpitem->rect.top != orgY) break; /* Other line */
1138 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1139 lpitem->rect.left += orgX - lpitem->rect.right;
1140 lpitem->rect.right = orgX;
1141 orgX = lpitem->rect.left;
1145 /***********************************************************************
1146 * MENU_DrawMenuItem
1148 * Draw a single menu item.
1150 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1151 UINT height, BOOL menuBar, UINT odaction )
1153 RECT rect;
1155 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1157 if (lpitem->fType & MF_SYSMENU)
1159 if( !IsIconic(hwnd) )
1160 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1161 return;
1164 /* Setup colors */
1166 if (lpitem->fState & MF_HILITE)
1168 if(menuBar) {
1169 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1170 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1171 } else {
1172 if(lpitem->fState & MF_GRAYED)
1173 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1174 else
1175 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1176 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1179 else
1181 if (lpitem->fState & MF_GRAYED)
1182 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1183 else
1184 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1185 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1188 if (lpitem->fType & MF_OWNERDRAW)
1191 ** Experimentation under Windows reveals that an owner-drawn
1192 ** menu is given the rectangle which includes the space it requested
1193 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1194 ** and a popup-menu arrow. This is the value of lpitem->rect.
1195 ** Windows will leave all drawing to the application except for
1196 ** the popup-menu arrow. Windows always draws that itself, after
1197 ** the menu owner has finished drawing.
1199 DRAWITEMSTRUCT dis;
1201 dis.CtlType = ODT_MENU;
1202 dis.CtlID = 0;
1203 dis.itemID = lpitem->wID;
1204 dis.itemData = (DWORD)lpitem->dwItemData;
1205 dis.itemState = 0;
1206 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1207 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1208 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1209 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1210 dis.hwndItem = (HWND)hmenu;
1211 dis.hDC = hdc;
1212 dis.rcItem = lpitem->rect;
1213 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1214 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1215 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1216 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1217 dis.rcItem.bottom);
1218 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1219 /* Fall through to draw popup-menu arrow */
1222 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1223 lpitem->rect.right,lpitem->rect.bottom);
1225 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1227 rect = lpitem->rect;
1229 if (!(lpitem->fType & MF_OWNERDRAW))
1231 if (lpitem->fState & MF_HILITE)
1233 if(menuBar)
1234 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1235 else
1236 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1238 else
1239 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1242 SetBkMode( hdc, TRANSPARENT );
1244 if (!(lpitem->fType & MF_OWNERDRAW))
1246 /* vertical separator */
1247 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1249 RECT rc = rect;
1250 rc.top = 3;
1251 rc.bottom = height - 3;
1252 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1255 /* horizontal separator */
1256 if (lpitem->fType & MF_SEPARATOR)
1258 RECT rc = rect;
1259 rc.left++;
1260 rc.right--;
1261 rc.top += SEPARATOR_HEIGHT / 2;
1262 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1263 return;
1267 /* helper lines for debugging */
1268 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1269 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1270 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1271 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1274 if (!menuBar)
1276 HBITMAP bm;
1277 INT y = rect.top + rect.bottom;
1278 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1279 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1281 if (!(lpitem->fType & MF_OWNERDRAW))
1283 /* New style MIIM_BITMAP */
1284 if (lpitem->hbmpItem)
1286 POPUPMENU *menu = MENU_GetMenu(hmenu);
1287 HBITMAP hbm = lpitem->hbmpItem;
1289 if (hbm == HBMMENU_CALLBACK)
1291 DRAWITEMSTRUCT drawItem;
1292 drawItem.CtlType = ODT_MENU;
1293 drawItem.CtlID = 0;
1294 drawItem.itemID = lpitem->wID;
1295 drawItem.itemAction = odaction;
1296 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1297 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1298 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1299 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1300 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1301 drawItem.hwndItem = (HWND)hmenu;
1302 drawItem.hDC = hdc;
1303 drawItem.rcItem = lpitem->rect;
1304 drawItem.itemData = lpitem->dwItemData;
1306 if (!(lpitem->fState & MF_CHECKED))
1307 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1309 } else {
1310 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1312 if (menu->dwStyle & MNS_CHECKORBMP)
1313 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1314 else
1315 rect.left += menu->maxBmpSize.cx;
1317 /* Draw the check mark
1319 * FIXME:
1320 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1322 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1323 if (bm) /* we have a custom bitmap */
1325 HDC hdcMem = CreateCompatibleDC( hdc );
1326 SelectObject( hdcMem, bm );
1327 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1328 check_bitmap_width, check_bitmap_height,
1329 hdcMem, 0, 0, SRCCOPY );
1330 DeleteDC( hdcMem );
1332 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1334 RECT r;
1335 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1336 HDC hdcMem = CreateCompatibleDC( hdc );
1337 SelectObject( hdcMem, bm );
1338 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1339 DrawFrameControl( hdcMem, &r, DFC_MENU,
1340 (lpitem->fType & MFT_RADIOCHECK) ?
1341 DFCS_MENUBULLET : DFCS_MENUCHECK );
1342 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1343 hdcMem, 0, 0, SRCCOPY );
1344 DeleteDC( hdcMem );
1345 DeleteObject( bm );
1349 /* Draw the popup-menu arrow */
1350 if (lpitem->fType & MF_POPUP)
1352 HDC hdcMem = CreateCompatibleDC( hdc );
1353 HBITMAP hOrigBitmap;
1355 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1356 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1357 (y - arrow_bitmap_height) / 2,
1358 arrow_bitmap_width, arrow_bitmap_height,
1359 hdcMem, 0, 0, SRCCOPY );
1360 SelectObject( hdcMem, hOrigBitmap );
1361 DeleteDC( hdcMem );
1364 rect.left += check_bitmap_width;
1365 rect.right -= arrow_bitmap_width;
1368 /* Done for owner-drawn */
1369 if (lpitem->fType & MF_OWNERDRAW)
1370 return;
1372 /* Draw the item text or bitmap */
1373 if (IS_BITMAP_ITEM(lpitem->fType))
1375 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1376 return;
1378 /* No bitmap - process text if present */
1379 else if (IS_STRING_ITEM(lpitem->fType))
1381 register int i;
1382 HFONT hfontOld = 0;
1384 UINT uFormat = (menuBar) ?
1385 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1386 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1388 if ( lpitem->fState & MFS_DEFAULT )
1390 hfontOld = SelectObject( hdc, hMenuFontBold);
1393 if (menuBar)
1395 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1396 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1399 for (i = 0; lpitem->text[i]; i++)
1400 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1401 break;
1403 if(lpitem->fState & MF_GRAYED)
1405 if (!(lpitem->fState & MF_HILITE) )
1407 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1408 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1409 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1410 --rect.left; --rect.top; --rect.right; --rect.bottom;
1412 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1415 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1417 /* paint the shortcut text */
1418 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1420 if (lpitem->text[i] == '\t')
1422 rect.left = lpitem->xTab;
1423 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1425 else
1427 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1430 if(lpitem->fState & MF_GRAYED)
1432 if (!(lpitem->fState & MF_HILITE) )
1434 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1435 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1436 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1437 --rect.left; --rect.top; --rect.right; --rect.bottom;
1439 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1441 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1444 if (hfontOld)
1445 SelectObject (hdc, hfontOld);
1450 /***********************************************************************
1451 * MENU_DrawPopupMenu
1453 * Paint a popup menu.
1455 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1457 HBRUSH hPrevBrush = 0;
1458 RECT rect;
1460 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1462 GetClientRect( hwnd, &rect );
1464 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1465 && (SelectObject( hdc, hMenuFont)))
1467 HPEN hPrevPen;
1469 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1471 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1472 if( hPrevPen )
1474 POPUPMENU *menu;
1476 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1478 /* draw menu items */
1480 menu = MENU_GetMenu( hmenu );
1481 if (menu && menu->nItems)
1483 MENUITEM *item;
1484 UINT u;
1486 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1487 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1488 menu->Height, FALSE, ODA_DRAWENTIRE );
1491 } else
1493 SelectObject( hdc, hPrevBrush );
1498 /***********************************************************************
1499 * MENU_DrawMenuBar
1501 * Paint a menu bar. Returns the height of the menu bar.
1502 * called from [windows/nonclient.c]
1504 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1505 BOOL suppress_draw)
1507 LPPOPUPMENU lppop;
1508 HFONT hfontOld = 0;
1509 HMENU hMenu = GetMenu(hwnd);
1511 lppop = MENU_GetMenu( hMenu );
1512 if (lppop == NULL || lprect == NULL)
1514 return GetSystemMetrics(SM_CYMENU);
1517 if (suppress_draw)
1519 hfontOld = SelectObject( hDC, hMenuFont);
1521 if (lppop->Height == 0)
1522 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1524 lprect->bottom = lprect->top + lppop->Height;
1526 if (hfontOld) SelectObject( hDC, hfontOld);
1527 return lppop->Height;
1529 else
1530 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1534 /***********************************************************************
1535 * MENU_ShowPopup
1537 * Display a popup menu.
1539 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1540 INT x, INT y, INT xanchor, INT yanchor )
1542 POPUPMENU *menu;
1543 UINT width, height;
1545 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1546 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1548 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1549 if (menu->FocusedItem != NO_SELECTED_ITEM)
1551 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1552 menu->FocusedItem = NO_SELECTED_ITEM;
1555 /* store the owner for DrawItem */
1556 menu->hwndOwner = hwndOwner;
1558 MENU_PopupMenuCalcSize( menu, hwndOwner );
1560 /* adjust popup menu pos so that it fits within the desktop */
1562 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1563 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1565 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1567 if( xanchor )
1568 x -= width - xanchor;
1569 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1570 x = GetSystemMetrics(SM_CXSCREEN) - width;
1572 if( x < 0 ) x = 0;
1574 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1576 if( yanchor )
1577 y -= height + yanchor;
1578 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1579 y = GetSystemMetrics(SM_CYSCREEN) - height;
1581 if( y < 0 ) y = 0;
1583 /* NOTE: In Windows, top menu popup is not owned. */
1584 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1585 WS_POPUP, x, y, width, height,
1586 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1587 (LPVOID)hmenu );
1588 if( !menu->hWnd ) return FALSE;
1589 if (!top_popup) top_popup = menu->hWnd;
1591 /* Display the window */
1593 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1594 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1595 UpdateWindow( menu->hWnd );
1596 return TRUE;
1600 /***********************************************************************
1601 * MENU_SelectItem
1603 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1604 BOOL sendMenuSelect, HMENU topmenu )
1606 LPPOPUPMENU lppop;
1607 HDC hdc;
1609 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1611 lppop = MENU_GetMenu( hmenu );
1612 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1614 if (lppop->FocusedItem == wIndex) return;
1615 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1616 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1617 if (!top_popup) top_popup = lppop->hWnd;
1619 SelectObject( hdc, hMenuFont);
1621 /* Clear previous highlighted item */
1622 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1624 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1625 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1626 lppop->Height, !(lppop->wFlags & MF_POPUP),
1627 ODA_SELECT );
1630 /* Highlight new item (if any) */
1631 lppop->FocusedItem = wIndex;
1632 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1634 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1635 lppop->items[wIndex].fState |= MF_HILITE;
1636 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1637 &lppop->items[wIndex], lppop->Height,
1638 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1640 if (sendMenuSelect)
1642 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1643 SendMessageW( hwndOwner, WM_MENUSELECT,
1644 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1645 ip->fType | ip->fState |
1646 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1649 else if (sendMenuSelect) {
1650 if(topmenu){
1651 int pos;
1652 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1653 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1654 MENUITEM *ip = &ptm->items[pos];
1655 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1656 ip->fType | ip->fState |
1657 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1661 ReleaseDC( lppop->hWnd, hdc );
1665 /***********************************************************************
1666 * MENU_MoveSelection
1668 * Moves currently selected item according to the offset parameter.
1669 * If there is no selection then it should select the last item if
1670 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1672 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1674 INT i;
1675 POPUPMENU *menu;
1677 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1679 menu = MENU_GetMenu( hmenu );
1680 if ((!menu) || (!menu->items)) return;
1682 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1684 if( menu->nItems == 1 ) return; else
1685 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1686 ; i += offset)
1687 if (!(menu->items[i].fType & MF_SEPARATOR))
1689 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1690 return;
1694 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1695 i >= 0 && i < menu->nItems ; i += offset)
1696 if (!(menu->items[i].fType & MF_SEPARATOR))
1698 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1699 return;
1704 /**********************************************************************
1705 * MENU_SetItemData
1707 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1708 * ModifyMenu().
1710 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1711 LPCWSTR str )
1713 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1715 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1716 TRACE("flags=%x str=%p\n", flags, str);
1718 if (IS_STRING_ITEM(flags))
1720 if (!str)
1722 flags |= MF_SEPARATOR;
1723 item->text = NULL;
1725 else
1727 LPWSTR text;
1728 /* Item beginning with a backspace is a help item */
1729 if (*str == '\b')
1731 flags |= MF_HELP;
1732 str++;
1734 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1735 return FALSE;
1736 strcpyW( text, str );
1737 item->text = text;
1740 else if (IS_BITMAP_ITEM(flags))
1741 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1742 else item->text = NULL;
1744 if (flags & MF_OWNERDRAW)
1745 item->dwItemData = (DWORD)str;
1746 else
1747 item->dwItemData = 0;
1749 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1750 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1752 if (flags & MF_POPUP)
1754 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1755 if (menu) menu->wFlags |= MF_POPUP;
1756 else
1758 item->wID = 0;
1759 item->hSubMenu = 0;
1760 item->fType = 0;
1761 item->fState = 0;
1762 return FALSE;
1766 item->wID = id;
1767 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1769 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1770 flags |= MF_POPUP; /* keep popup */
1772 item->fType = flags & TYPE_MASK;
1773 item->fState = (flags & STATE_MASK) &
1774 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1777 /* Don't call SetRectEmpty here! */
1780 HeapFree( GetProcessHeap(), 0, prevText );
1782 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1783 return TRUE;
1787 /**********************************************************************
1788 * MENU_InsertItem
1790 * Insert (allocate) a new item into a menu.
1792 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1794 MENUITEM *newItems;
1795 POPUPMENU *menu;
1797 if (!(menu = MENU_GetMenu(hMenu)))
1798 return NULL;
1800 /* Find where to insert new item */
1802 if (flags & MF_BYPOSITION) {
1803 if (pos > menu->nItems)
1804 pos = menu->nItems;
1805 } else {
1806 if (!MENU_FindItem( &hMenu, &pos, flags ))
1807 pos = menu->nItems;
1808 else {
1809 if (!(menu = MENU_GetMenu( hMenu )))
1810 return NULL;
1814 /* Create new items array */
1816 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1817 if (!newItems)
1819 WARN("allocation failed\n" );
1820 return NULL;
1822 if (menu->nItems > 0)
1824 /* Copy the old array into the new one */
1825 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1826 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1827 (menu->nItems-pos)*sizeof(MENUITEM) );
1828 HeapFree( GetProcessHeap(), 0, menu->items );
1830 menu->items = newItems;
1831 menu->nItems++;
1832 memset( &newItems[pos], 0, sizeof(*newItems) );
1833 menu->Height = 0; /* force size recalculate */
1834 return &newItems[pos];
1838 /**********************************************************************
1839 * MENU_ParseResource
1841 * Parse a standard menu resource and add items to the menu.
1842 * Return a pointer to the end of the resource.
1844 * NOTE: flags is equivalent to the mtOption field
1846 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1848 WORD flags, id = 0;
1849 LPCSTR str;
1853 flags = GET_WORD(res);
1854 res += sizeof(WORD);
1855 if (!(flags & MF_POPUP))
1857 id = GET_WORD(res);
1858 res += sizeof(WORD);
1860 str = res;
1861 if (!unicode) res += strlen(str) + 1;
1862 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1863 if (flags & MF_POPUP)
1865 HMENU hSubMenu = CreatePopupMenu();
1866 if (!hSubMenu) return NULL;
1867 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1868 return NULL;
1869 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1870 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1872 else /* Not a popup */
1874 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1875 else AppendMenuW( hMenu, flags, id,
1876 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1878 } while (!(flags & MF_END));
1879 return res;
1883 /**********************************************************************
1884 * MENUEX_ParseResource
1886 * Parse an extended menu resource and add items to the menu.
1887 * Return a pointer to the end of the resource.
1889 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1891 WORD resinfo;
1892 do {
1893 MENUITEMINFOW mii;
1895 mii.cbSize = sizeof(mii);
1896 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1897 mii.fType = GET_DWORD(res);
1898 res += sizeof(DWORD);
1899 mii.fState = GET_DWORD(res);
1900 res += sizeof(DWORD);
1901 mii.wID = GET_DWORD(res);
1902 res += sizeof(DWORD);
1903 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1904 res += sizeof(WORD);
1905 /* Align the text on a word boundary. */
1906 res += (~((int)res - 1)) & 1;
1907 mii.dwTypeData = (LPWSTR) res;
1908 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1909 /* Align the following fields on a dword boundary. */
1910 res += (~((int)res - 1)) & 3;
1912 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1913 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1915 if (resinfo & 1) { /* Pop-up? */
1916 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1917 res += sizeof(DWORD);
1918 mii.hSubMenu = CreatePopupMenu();
1919 if (!mii.hSubMenu)
1920 return NULL;
1921 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1922 DestroyMenu(mii.hSubMenu);
1923 return NULL;
1925 mii.fMask |= MIIM_SUBMENU;
1926 mii.fType |= MF_POPUP;
1928 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1930 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1931 mii.wID, mii.fType);
1932 mii.fType |= MF_SEPARATOR;
1934 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1935 } while (!(resinfo & MF_END));
1936 return res;
1940 /***********************************************************************
1941 * MENU_GetSubPopup
1943 * Return the handle of the selected sub-popup menu (if any).
1945 static HMENU MENU_GetSubPopup( HMENU hmenu )
1947 POPUPMENU *menu;
1948 MENUITEM *item;
1950 menu = MENU_GetMenu( hmenu );
1952 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1954 item = &menu->items[menu->FocusedItem];
1955 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1956 return item->hSubMenu;
1957 return 0;
1961 /***********************************************************************
1962 * MENU_HideSubPopups
1964 * Hide the sub-popup menus of this menu.
1966 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1967 BOOL sendMenuSelect )
1969 POPUPMENU *menu = MENU_GetMenu( hmenu );
1971 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1973 if (menu && top_popup)
1975 HMENU hsubmenu;
1976 POPUPMENU *submenu;
1977 MENUITEM *item;
1979 if (menu->FocusedItem != NO_SELECTED_ITEM)
1981 item = &menu->items[menu->FocusedItem];
1982 if (!(item->fType & MF_POPUP) ||
1983 !(item->fState & MF_MOUSESELECT)) return;
1984 item->fState &= ~MF_MOUSESELECT;
1985 hsubmenu = item->hSubMenu;
1986 } else return;
1988 submenu = MENU_GetMenu( hsubmenu );
1989 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1990 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1991 DestroyWindow( submenu->hWnd );
1992 submenu->hWnd = 0;
1997 /***********************************************************************
1998 * MENU_ShowSubPopup
2000 * Display the sub-menu of the selected item of this menu.
2001 * Return the handle of the submenu, or hmenu if no submenu to display.
2003 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2004 BOOL selectFirst, UINT wFlags )
2006 RECT rect;
2007 POPUPMENU *menu;
2008 MENUITEM *item;
2009 HDC hdc;
2011 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2013 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2015 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2017 item = &menu->items[menu->FocusedItem];
2018 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2019 return hmenu;
2021 /* message must be sent before using item,
2022 because nearly everything may be changed by the application ! */
2024 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2025 if (!(wFlags & TPM_NONOTIFY))
2026 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2027 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2029 item = &menu->items[menu->FocusedItem];
2030 rect = item->rect;
2032 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2033 if (!(item->fState & MF_HILITE))
2035 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2036 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2038 SelectObject( hdc, hMenuFont);
2040 item->fState |= MF_HILITE;
2041 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2042 ReleaseDC( menu->hWnd, hdc );
2044 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2045 item->rect = rect;
2047 item->fState |= MF_MOUSESELECT;
2049 if (IS_SYSTEM_MENU(menu))
2051 MENU_InitSysMenuPopup(item->hSubMenu,
2052 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2053 GetClassLongW( menu->hWnd, GCL_STYLE));
2055 NC_GetSysPopupPos( menu->hWnd, &rect );
2056 rect.top = rect.bottom;
2057 rect.right = GetSystemMetrics(SM_CXSIZE);
2058 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2060 else
2062 GetWindowRect( menu->hWnd, &rect );
2063 if (menu->wFlags & MF_POPUP)
2065 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2066 rect.top += item->rect.top;
2067 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2068 rect.bottom = item->rect.top - item->rect.bottom;
2070 else
2072 rect.left += item->rect.left;
2073 rect.top += item->rect.bottom;
2074 rect.right = item->rect.right - item->rect.left;
2075 rect.bottom = item->rect.bottom - item->rect.top;
2079 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2080 rect.left, rect.top, rect.right, rect.bottom );
2081 if (selectFirst)
2082 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2083 return item->hSubMenu;
2088 /**********************************************************************
2089 * MENU_IsMenuActive
2091 HWND MENU_IsMenuActive(void)
2093 return top_popup;
2096 /***********************************************************************
2097 * MENU_PtMenu
2099 * Walks menu chain trying to find a menu pt maps to.
2101 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2103 POPUPMENU *menu = MENU_GetMenu( hMenu );
2104 UINT item = menu->FocusedItem;
2105 HMENU ret;
2107 /* try subpopup first (if any) */
2108 ret = (item != NO_SELECTED_ITEM &&
2109 (menu->items[item].fType & MF_POPUP) &&
2110 (menu->items[item].fState & MF_MOUSESELECT))
2111 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2113 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2115 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2116 if( menu->wFlags & MF_POPUP )
2118 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2120 else if (ht == HTSYSMENU)
2121 ret = get_win_sys_menu( menu->hWnd );
2122 else if (ht == HTMENU)
2123 ret = GetMenu( menu->hWnd );
2125 return ret;
2128 /***********************************************************************
2129 * MENU_ExecFocusedItem
2131 * Execute a menu item (for instance when user pressed Enter).
2132 * Return the wID of the executed item. Otherwise, -1 indicating
2133 * that no menu item was executed;
2134 * Have to receive the flags for the TrackPopupMenu options to avoid
2135 * sending unwanted message.
2138 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2140 MENUITEM *item;
2141 POPUPMENU *menu = MENU_GetMenu( hMenu );
2143 TRACE("%p hmenu=%p\n", pmt, hMenu);
2145 if (!menu || !menu->nItems ||
2146 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2148 item = &menu->items[menu->FocusedItem];
2150 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2152 if (!(item->fType & MF_POPUP))
2154 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2156 /* If TPM_RETURNCMD is set you return the id, but
2157 do not send a message to the owner */
2158 if(!(wFlags & TPM_RETURNCMD))
2160 if( menu->wFlags & MF_SYSMENU )
2161 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2162 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2163 else
2164 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2166 return item->wID;
2169 else
2170 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2172 return -1;
2175 /***********************************************************************
2176 * MENU_SwitchTracking
2178 * Helper function for menu navigation routines.
2180 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2182 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2183 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2185 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2187 if( pmt->hTopMenu != hPtMenu &&
2188 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2190 /* both are top level menus (system and menu-bar) */
2191 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2192 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2193 pmt->hTopMenu = hPtMenu;
2195 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2196 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2200 /***********************************************************************
2201 * MENU_ButtonDown
2203 * Return TRUE if we can go on with menu tracking.
2205 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2207 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2209 if (hPtMenu)
2211 UINT id = 0;
2212 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2213 MENUITEM *item;
2215 if( IS_SYSTEM_MENU(ptmenu) )
2216 item = ptmenu->items;
2217 else
2218 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2220 if( item )
2222 if( ptmenu->FocusedItem != id )
2223 MENU_SwitchTracking( pmt, hPtMenu, id );
2225 /* If the popup menu is not already "popped" */
2226 if(!(item->fState & MF_MOUSESELECT ))
2228 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2231 return TRUE;
2233 /* Else the click was on the menu bar, finish the tracking */
2235 return FALSE;
2238 /***********************************************************************
2239 * MENU_ButtonUp
2241 * Return the value of MENU_ExecFocusedItem if
2242 * the selected item was not a popup. Else open the popup.
2243 * A -1 return value indicates that we go on with menu tracking.
2246 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2248 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2250 if (hPtMenu)
2252 UINT id = 0;
2253 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2254 MENUITEM *item;
2256 if( IS_SYSTEM_MENU(ptmenu) )
2257 item = ptmenu->items;
2258 else
2259 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2261 if( item && (ptmenu->FocusedItem == id ))
2263 if( !(item->fType & MF_POPUP) )
2264 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2266 /* If we are dealing with the top-level menu */
2267 /* and this is a click on an already "popped" item: */
2268 /* Stop the menu tracking and close the opened submenus */
2269 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2270 return 0;
2272 ptmenu->bTimeToHide = TRUE;
2274 return -1;
2278 /***********************************************************************
2279 * MENU_MouseMove
2281 * Return TRUE if we can go on with menu tracking.
2283 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2285 UINT id = NO_SELECTED_ITEM;
2286 POPUPMENU *ptmenu = NULL;
2288 if( hPtMenu )
2290 ptmenu = MENU_GetMenu( hPtMenu );
2291 if( IS_SYSTEM_MENU(ptmenu) )
2292 id = 0;
2293 else
2294 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2297 if( id == NO_SELECTED_ITEM )
2299 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2300 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2303 else if( ptmenu->FocusedItem != id )
2305 MENU_SwitchTracking( pmt, hPtMenu, id );
2306 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2308 return TRUE;
2312 /***********************************************************************
2313 * MENU_SetCapture
2315 static void MENU_SetCapture( HWND hwnd )
2317 HWND previous = 0;
2319 SERVER_START_REQ( set_capture_window )
2321 req->handle = hwnd;
2322 req->flags = CAPTURE_MENU;
2323 if (!wine_server_call_err( req ))
2325 previous = reply->previous;
2326 hwnd = reply->full_handle;
2329 SERVER_END_REQ;
2331 if (previous && previous != hwnd)
2332 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2336 /***********************************************************************
2337 * MENU_DoNextMenu
2339 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2341 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2343 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2345 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2346 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2348 MDINEXTMENU next_menu;
2349 HMENU hNewMenu;
2350 HWND hNewWnd;
2351 UINT id = 0;
2353 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2354 next_menu.hmenuNext = 0;
2355 next_menu.hwndNext = 0;
2356 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2358 TRACE("%p [%p] -> %p [%p]\n",
2359 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2361 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2363 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2364 hNewWnd = pmt->hOwnerWnd;
2365 if( IS_SYSTEM_MENU(menu) )
2367 /* switch to the menu bar */
2369 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2371 if( vk == VK_LEFT )
2373 menu = MENU_GetMenu( hNewMenu );
2374 id = menu->nItems - 1;
2377 else if (style & WS_SYSMENU )
2379 /* switch to the system menu */
2380 hNewMenu = get_win_sys_menu( hNewWnd );
2382 else return FALSE;
2384 else /* application returned a new menu to switch to */
2386 hNewMenu = next_menu.hmenuNext;
2387 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2389 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2391 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2393 if (style & WS_SYSMENU &&
2394 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2396 /* get the real system menu */
2397 hNewMenu = get_win_sys_menu(hNewWnd);
2399 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2401 /* FIXME: Not sure what to do here;
2402 * perhaps try to track hNewMenu as a popup? */
2404 TRACE(" -- got confused.\n");
2405 return FALSE;
2408 else return FALSE;
2411 if( hNewMenu != pmt->hTopMenu )
2413 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2414 FALSE, 0 );
2415 if( pmt->hCurrentMenu != pmt->hTopMenu )
2416 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2419 if( hNewWnd != pmt->hOwnerWnd )
2421 pmt->hOwnerWnd = hNewWnd;
2422 MENU_SetCapture( pmt->hOwnerWnd );
2425 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2426 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2428 return TRUE;
2430 return FALSE;
2433 /***********************************************************************
2434 * MENU_SuspendPopup
2436 * The idea is not to show the popup if the next input message is
2437 * going to hide it anyway.
2439 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2441 MSG msg;
2443 msg.hwnd = pmt->hOwnerWnd;
2445 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2446 pmt->trackFlags |= TF_SKIPREMOVE;
2448 switch( uMsg )
2450 case WM_KEYDOWN:
2451 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2452 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2454 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2455 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2456 if( msg.message == WM_KEYDOWN &&
2457 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2459 pmt->trackFlags |= TF_SUSPENDPOPUP;
2460 return TRUE;
2463 break;
2466 /* failures go through this */
2467 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2468 return FALSE;
2471 /***********************************************************************
2472 * MENU_KeyEscape
2474 * Handle a VK_ESCAPE key event in a menu.
2476 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2478 BOOL bEndMenu = TRUE;
2480 if (pmt->hCurrentMenu != pmt->hTopMenu)
2482 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2484 if (menu->wFlags & MF_POPUP)
2486 HMENU hmenutmp, hmenuprev;
2488 hmenuprev = hmenutmp = pmt->hTopMenu;
2490 /* close topmost popup */
2491 while (hmenutmp != pmt->hCurrentMenu)
2493 hmenuprev = hmenutmp;
2494 hmenutmp = MENU_GetSubPopup( hmenuprev );
2497 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2498 pmt->hCurrentMenu = hmenuprev;
2499 bEndMenu = FALSE;
2503 return bEndMenu;
2506 /***********************************************************************
2507 * MENU_KeyLeft
2509 * Handle a VK_LEFT key event in a menu.
2511 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2513 POPUPMENU *menu;
2514 HMENU hmenutmp, hmenuprev;
2515 UINT prevcol;
2517 hmenuprev = hmenutmp = pmt->hTopMenu;
2518 menu = MENU_GetMenu( hmenutmp );
2520 /* Try to move 1 column left (if possible) */
2521 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2522 NO_SELECTED_ITEM ) {
2524 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2525 prevcol, TRUE, 0 );
2526 return;
2529 /* close topmost popup */
2530 while (hmenutmp != pmt->hCurrentMenu)
2532 hmenuprev = hmenutmp;
2533 hmenutmp = MENU_GetSubPopup( hmenuprev );
2536 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2537 pmt->hCurrentMenu = hmenuprev;
2539 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2541 /* move menu bar selection if no more popups are left */
2543 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2544 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2546 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2548 /* A sublevel menu was displayed - display the next one
2549 * unless there is another displacement coming up */
2551 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2552 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2553 pmt->hTopMenu, TRUE, wFlags);
2559 /***********************************************************************
2560 * MENU_KeyRight
2562 * Handle a VK_RIGHT key event in a menu.
2564 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2566 HMENU hmenutmp;
2567 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2568 UINT nextcol;
2570 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2571 pmt->hCurrentMenu,
2572 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2573 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2575 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2577 /* If already displaying a popup, try to display sub-popup */
2579 hmenutmp = pmt->hCurrentMenu;
2580 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2582 /* if subpopup was displayed then we are done */
2583 if (hmenutmp != pmt->hCurrentMenu) return;
2586 /* Check to see if there's another column */
2587 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2588 NO_SELECTED_ITEM ) {
2589 TRACE("Going to %d.\n", nextcol );
2590 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2591 nextcol, TRUE, 0 );
2592 return;
2595 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2597 if( pmt->hCurrentMenu != pmt->hTopMenu )
2599 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2600 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2601 } else hmenutmp = 0;
2603 /* try to move to the next item */
2604 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2605 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2607 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2608 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2609 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2610 pmt->hTopMenu, TRUE, wFlags);
2614 /***********************************************************************
2615 * MENU_TrackMenu
2617 * Menu tracking code.
2619 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2620 HWND hwnd, const RECT *lprect )
2622 MSG msg;
2623 POPUPMENU *menu;
2624 BOOL fRemove;
2625 INT executedMenuId = -1;
2626 MTRACKER mt;
2627 BOOL enterIdleSent = FALSE;
2629 mt.trackFlags = 0;
2630 mt.hCurrentMenu = hmenu;
2631 mt.hTopMenu = hmenu;
2632 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2633 mt.pt.x = x;
2634 mt.pt.y = y;
2636 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2637 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2638 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2640 fEndMenu = FALSE;
2641 if (!(menu = MENU_GetMenu( hmenu )))
2643 WARN("Invalid menu handle %p\n", hmenu);
2644 SetLastError(ERROR_INVALID_MENU_HANDLE);
2645 return FALSE;
2648 if (wFlags & TPM_BUTTONDOWN)
2650 /* Get the result in order to start the tracking or not */
2651 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2652 fEndMenu = !fRemove;
2655 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2657 MENU_SetCapture( mt.hOwnerWnd );
2659 while (!fEndMenu)
2661 menu = MENU_GetMenu( mt.hCurrentMenu );
2662 if (!menu) /* sometimes happens if I do a window manager close */
2663 break;
2665 /* we have to keep the message in the queue until it's
2666 * clear that menu loop is not over yet. */
2668 for (;;)
2670 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2672 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2673 /* remove the message from the queue */
2674 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2676 else
2678 if (!enterIdleSent)
2680 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2681 enterIdleSent = TRUE;
2682 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2684 WaitMessage();
2688 /* check if EndMenu() tried to cancel us, by posting this message */
2689 if(msg.message == WM_CANCELMODE)
2691 /* we are now out of the loop */
2692 fEndMenu = TRUE;
2694 /* remove the message from the queue */
2695 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2697 /* break out of internal loop, ala ESCAPE */
2698 break;
2701 TranslateMessage( &msg );
2702 mt.pt = msg.pt;
2704 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2705 enterIdleSent=FALSE;
2707 fRemove = FALSE;
2708 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2711 * Use the mouse coordinates in lParam instead of those in the MSG
2712 * struct to properly handle synthetic messages. They are already
2713 * in screen coordinates.
2715 mt.pt.x = (short)LOWORD(msg.lParam);
2716 mt.pt.y = (short)HIWORD(msg.lParam);
2718 /* Find a menu for this mouse event */
2719 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2721 switch(msg.message)
2723 /* no WM_NC... messages in captured state */
2725 case WM_RBUTTONDBLCLK:
2726 case WM_RBUTTONDOWN:
2727 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2728 /* fall through */
2729 case WM_LBUTTONDBLCLK:
2730 case WM_LBUTTONDOWN:
2731 /* If the message belongs to the menu, removes it from the queue */
2732 /* Else, end menu tracking */
2733 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2734 fEndMenu = !fRemove;
2735 break;
2737 case WM_RBUTTONUP:
2738 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2739 /* fall through */
2740 case WM_LBUTTONUP:
2741 /* Check if a menu was selected by the mouse */
2742 if (hmenu)
2744 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2746 /* End the loop if executedMenuId is an item ID */
2747 /* or if the job was done (executedMenuId = 0). */
2748 fEndMenu = fRemove = (executedMenuId != -1);
2750 /* No menu was selected by the mouse */
2751 /* if the function was called by TrackPopupMenu, continue
2752 with the menu tracking. If not, stop it */
2753 else
2754 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2756 break;
2758 case WM_MOUSEMOVE:
2759 /* the selected menu item must be changed every time */
2760 /* the mouse moves. */
2762 if (hmenu)
2763 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2765 } /* switch(msg.message) - mouse */
2767 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2769 fRemove = TRUE; /* Keyboard messages are always removed */
2770 switch(msg.message)
2772 case WM_KEYDOWN:
2773 case WM_SYSKEYDOWN:
2774 switch(msg.wParam)
2776 case VK_MENU:
2777 fEndMenu = TRUE;
2778 break;
2780 case VK_HOME:
2781 case VK_END:
2782 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2783 NO_SELECTED_ITEM, FALSE, 0 );
2784 /* fall through */
2785 case VK_UP:
2786 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2787 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2788 break;
2790 case VK_DOWN: /* If on menu bar, pull-down the menu */
2792 menu = MENU_GetMenu( mt.hCurrentMenu );
2793 if (!(menu->wFlags & MF_POPUP))
2794 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2795 else /* otherwise try to move selection */
2796 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2797 break;
2799 case VK_LEFT:
2800 MENU_KeyLeft( &mt, wFlags );
2801 break;
2803 case VK_RIGHT:
2804 MENU_KeyRight( &mt, wFlags );
2805 break;
2807 case VK_ESCAPE:
2808 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2809 break;
2811 case VK_F1:
2813 HELPINFO hi;
2814 hi.cbSize = sizeof(HELPINFO);
2815 hi.iContextType = HELPINFO_MENUITEM;
2816 if (menu->FocusedItem == NO_SELECTED_ITEM)
2817 hi.iCtrlId = 0;
2818 else
2819 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2820 hi.hItemHandle = hmenu;
2821 hi.dwContextId = menu->dwContextHelpID;
2822 hi.MousePos = msg.pt;
2823 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2824 break;
2827 default:
2828 break;
2830 break; /* WM_KEYDOWN */
2832 case WM_CHAR:
2833 case WM_SYSCHAR:
2835 UINT pos;
2837 if (msg.wParam == '\r' || msg.wParam == ' ')
2839 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2840 fEndMenu = (executedMenuId != -1);
2842 break;
2845 /* Hack to avoid control chars. */
2846 /* We will find a better way real soon... */
2847 if (msg.wParam < 32) break;
2849 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2850 LOWORD(msg.wParam), FALSE );
2851 if (pos == (UINT)-2) fEndMenu = TRUE;
2852 else if (pos == (UINT)-1) MessageBeep(0);
2853 else
2855 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2856 TRUE, 0 );
2857 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2858 fEndMenu = (executedMenuId != -1);
2861 break;
2862 } /* switch(msg.message) - kbd */
2864 else
2866 DispatchMessageW( &msg );
2869 if (!fEndMenu) fRemove = TRUE;
2871 /* finally remove message from the queue */
2873 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2874 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2875 else mt.trackFlags &= ~TF_SKIPREMOVE;
2878 MENU_SetCapture(0); /* release the capture */
2880 /* If dropdown is still painted and the close box is clicked on
2881 then the menu will be destroyed as part of the DispatchMessage above.
2882 This will then invalidate the menu handle in mt.hTopMenu. We should
2883 check for this first. */
2884 if( IsMenu( mt.hTopMenu ) )
2886 menu = MENU_GetMenu( mt.hTopMenu );
2888 if( IsWindow( mt.hOwnerWnd ) )
2890 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2892 if (menu && (menu->wFlags & MF_POPUP))
2894 DestroyWindow( menu->hWnd );
2895 menu->hWnd = 0;
2897 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2898 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2901 /* Reset the variable for hiding menu */
2902 if( menu ) menu->bTimeToHide = FALSE;
2905 /* The return value is only used by TrackPopupMenu */
2906 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2907 if (executedMenuId == -1) executedMenuId = 0;
2908 return executedMenuId;
2911 /***********************************************************************
2912 * MENU_InitTracking
2914 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2916 POPUPMENU *menu;
2918 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2920 HideCaret(0);
2922 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2923 if (!(wFlags & TPM_NONOTIFY))
2924 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2926 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2928 if (!(wFlags & TPM_NONOTIFY))
2930 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2931 /* If an app changed/recreated menu bar entries in WM_INITMENU
2932 * menu sizes will be recalculated once the menu created/shown.
2936 /* This makes the menus of applications built with Delphi work.
2937 * It also enables menus to be displayed in more than one window,
2938 * but there are some bugs left that need to be fixed in this case.
2940 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2942 return TRUE;
2944 /***********************************************************************
2945 * MENU_ExitTracking
2947 static BOOL MENU_ExitTracking(HWND hWnd)
2949 TRACE("hwnd=%p\n", hWnd);
2951 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2952 ShowCaret(0);
2953 top_popup = 0;
2954 return TRUE;
2957 /***********************************************************************
2958 * MENU_TrackMouseMenuBar
2960 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2962 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2964 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2965 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2967 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2969 if (IsMenu(hMenu))
2971 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2972 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2973 MENU_ExitTracking(hWnd);
2978 /***********************************************************************
2979 * MENU_TrackKbdMenuBar
2981 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2983 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2985 UINT uItem = NO_SELECTED_ITEM;
2986 HMENU hTrackMenu;
2987 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2989 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2991 /* find window that has a menu */
2993 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
2994 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
2996 /* check if we have to track a system menu */
2998 hTrackMenu = GetMenu( hwnd );
2999 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3001 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3002 hTrackMenu = get_win_sys_menu( hwnd );
3003 uItem = 0;
3004 wParam |= HTSYSMENU; /* prevent item lookup */
3007 if (!IsMenu( hTrackMenu )) return;
3009 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3011 if( wChar && wChar != ' ' )
3013 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3014 if ( uItem >= (UINT)(-2) )
3016 if( uItem == (UINT)(-1) ) MessageBeep(0);
3017 /* schedule end of menu tracking */
3018 wFlags |= TF_ENDMENU;
3019 goto track_menu;
3023 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3025 if (wParam & HTSYSMENU)
3027 /* prevent sysmenu activation for managed windows on Alt down/up */
3028 if (GetPropA( hwnd, "__wine_x11_managed" ))
3029 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3031 else
3033 if( uItem == NO_SELECTED_ITEM )
3034 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3035 else
3036 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3039 track_menu:
3040 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3041 MENU_ExitTracking( hwnd );
3045 /**********************************************************************
3046 * TrackPopupMenu (USER32.@)
3048 * Like the win32 API, the function return the command ID only if the
3049 * flag TPM_RETURNCMD is on.
3052 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3053 INT nReserved, HWND hWnd, const RECT *lpRect )
3055 BOOL ret = FALSE;
3057 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3059 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3060 if (!(wFlags & TPM_NONOTIFY))
3061 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3063 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3064 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3065 MENU_ExitTracking(hWnd);
3067 return ret;
3070 /**********************************************************************
3071 * TrackPopupMenuEx (USER32.@)
3073 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3074 HWND hWnd, LPTPMPARAMS lpTpm )
3076 FIXME("not fully implemented\n" );
3077 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3078 lpTpm ? &lpTpm->rcExclude : NULL );
3081 /***********************************************************************
3082 * PopupMenuWndProc
3084 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3086 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3088 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3090 switch(message)
3092 case WM_CREATE:
3094 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3095 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3096 return 0;
3099 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3100 return MA_NOACTIVATE;
3102 case WM_PAINT:
3104 PAINTSTRUCT ps;
3105 BeginPaint( hwnd, &ps );
3106 MENU_DrawPopupMenu( hwnd, ps.hdc,
3107 (HMENU)GetWindowLongW( hwnd, 0 ) );
3108 EndPaint( hwnd, &ps );
3109 return 0;
3111 case WM_ERASEBKGND:
3112 return 1;
3114 case WM_DESTROY:
3115 /* zero out global pointer in case resident popup window was destroyed. */
3116 if (hwnd == top_popup) top_popup = 0;
3117 break;
3119 case WM_SHOWWINDOW:
3121 if( wParam )
3123 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3125 else
3126 SetWindowLongW( hwnd, 0, 0 );
3127 break;
3129 case MM_SETMENUHANDLE:
3130 SetWindowLongW( hwnd, 0, wParam );
3131 break;
3133 case MM_GETMENUHANDLE:
3134 return GetWindowLongW( hwnd, 0 );
3136 default:
3137 return DefWindowProcW( hwnd, message, wParam, lParam );
3139 return 0;
3143 /***********************************************************************
3144 * MENU_GetMenuBarHeight
3146 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3148 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3149 INT orgX, INT orgY )
3151 HDC hdc;
3152 RECT rectBar;
3153 LPPOPUPMENU lppop;
3155 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3157 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3159 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3160 SelectObject( hdc, hMenuFont);
3161 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3162 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3163 ReleaseDC( hwnd, hdc );
3164 return lppop->Height;
3168 /*******************************************************************
3169 * ChangeMenuA (USER32.@)
3171 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3172 UINT id, UINT flags )
3174 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3175 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3176 id, data );
3177 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3178 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3179 id, data );
3180 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3181 flags & MF_BYPOSITION ? pos : id,
3182 flags & ~MF_REMOVE );
3183 /* Default: MF_INSERT */
3184 return InsertMenuA( hMenu, pos, flags, id, data );
3188 /*******************************************************************
3189 * ChangeMenuW (USER32.@)
3191 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3192 UINT id, UINT flags )
3194 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3195 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3196 id, data );
3197 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3198 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3199 id, data );
3200 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3201 flags & MF_BYPOSITION ? pos : id,
3202 flags & ~MF_REMOVE );
3203 /* Default: MF_INSERT */
3204 return InsertMenuW( hMenu, pos, flags, id, data );
3208 /*******************************************************************
3209 * CheckMenuItem (USER32.@)
3211 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3213 MENUITEM *item;
3214 DWORD ret;
3216 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3217 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3218 ret = item->fState & MF_CHECKED;
3219 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3220 else item->fState &= ~MF_CHECKED;
3221 return ret;
3225 /**********************************************************************
3226 * EnableMenuItem (USER32.@)
3228 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3230 UINT oldflags;
3231 MENUITEM *item;
3232 POPUPMENU *menu;
3234 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3236 /* Get the Popupmenu to access the owner menu */
3237 if (!(menu = MENU_GetMenu(hMenu)))
3238 return (UINT)-1;
3240 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3241 return (UINT)-1;
3243 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3244 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3246 /* If the close item in the system menu change update the close button */
3247 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3249 if (menu->hSysMenuOwner != 0)
3251 POPUPMENU* parentMenu;
3253 /* Get the parent menu to access*/
3254 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3255 return (UINT)-1;
3257 /* Refresh the frame to reflect the change*/
3258 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3259 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3263 return oldflags;
3267 /*******************************************************************
3268 * GetMenuStringA (USER32.@)
3270 INT WINAPI GetMenuStringA(
3271 HMENU hMenu, /* [in] menuhandle */
3272 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3273 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3274 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3275 UINT wFlags /* [in] MF_ flags */
3277 MENUITEM *item;
3279 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3280 if (str && nMaxSiz) str[0] = '\0';
3281 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3282 if (!IS_STRING_ITEM(item->fType)) return 0;
3283 if (!str || !nMaxSiz) return strlenW(item->text);
3284 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3285 str[nMaxSiz-1] = 0;
3286 TRACE("returning '%s'\n", str );
3287 return strlen(str);
3291 /*******************************************************************
3292 * GetMenuStringW (USER32.@)
3294 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3295 LPWSTR str, INT nMaxSiz, UINT wFlags )
3297 MENUITEM *item;
3299 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3300 if (str && nMaxSiz) str[0] = '\0';
3301 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3302 if (!IS_STRING_ITEM(item->fType)) return 0;
3303 if (!str || !nMaxSiz) return strlenW(item->text);
3304 lstrcpynW( str, item->text, nMaxSiz );
3305 return strlenW(str);
3309 /**********************************************************************
3310 * HiliteMenuItem (USER32.@)
3312 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3313 UINT wHilite )
3315 LPPOPUPMENU menu;
3316 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3317 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3318 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3319 if (menu->FocusedItem == wItemID) return TRUE;
3320 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3321 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3322 return TRUE;
3326 /**********************************************************************
3327 * GetMenuState (USER32.@)
3329 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3331 MENUITEM *item;
3332 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3333 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3334 debug_print_menuitem (" item: ", item, "");
3335 if (item->fType & MF_POPUP)
3337 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3338 if (!menu) return -1;
3339 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3341 else
3343 /* We used to (from way back then) mask the result to 0xff. */
3344 /* I don't know why and it seems wrong as the documented */
3345 /* return flag MF_SEPARATOR is outside that mask. */
3346 return (item->fType | item->fState);
3351 /**********************************************************************
3352 * GetMenuItemCount (USER32.@)
3354 INT WINAPI GetMenuItemCount( HMENU hMenu )
3356 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3357 if (!menu) return -1;
3358 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3359 return menu->nItems;
3363 /**********************************************************************
3364 * GetMenuItemID (USER32.@)
3366 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3368 MENUITEM * lpmi;
3370 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3371 if (lpmi->fType & MF_POPUP) return -1;
3372 return lpmi->wID;
3377 /*******************************************************************
3378 * InsertMenuW (USER32.@)
3380 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3381 UINT_PTR id, LPCWSTR str )
3383 MENUITEM *item;
3385 if (IS_STRING_ITEM(flags) && str)
3386 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3387 hMenu, pos, flags, id, debugstr_w(str) );
3388 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3389 hMenu, pos, flags, id, (DWORD)str );
3391 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3393 if (!(MENU_SetItemData( item, flags, id, str )))
3395 RemoveMenu( hMenu, pos, flags );
3396 return FALSE;
3399 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3400 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3402 item->hCheckBit = item->hUnCheckBit = 0;
3403 return TRUE;
3407 /*******************************************************************
3408 * InsertMenuA (USER32.@)
3410 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3411 UINT_PTR id, LPCSTR str )
3413 BOOL ret = FALSE;
3415 if (IS_STRING_ITEM(flags) && str)
3417 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3418 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3419 if (newstr)
3421 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3422 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3423 HeapFree( GetProcessHeap(), 0, newstr );
3425 return ret;
3427 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3431 /*******************************************************************
3432 * AppendMenuA (USER32.@)
3434 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3435 UINT_PTR id, LPCSTR data )
3437 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3441 /*******************************************************************
3442 * AppendMenuW (USER32.@)
3444 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3445 UINT_PTR id, LPCWSTR data )
3447 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3451 /**********************************************************************
3452 * RemoveMenu (USER32.@)
3454 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3456 LPPOPUPMENU menu;
3457 MENUITEM *item;
3459 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3460 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3461 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3463 /* Remove item */
3465 MENU_FreeItemData( item );
3467 if (--menu->nItems == 0)
3469 HeapFree( GetProcessHeap(), 0, menu->items );
3470 menu->items = NULL;
3472 else
3474 while(nPos < menu->nItems)
3476 *item = *(item+1);
3477 item++;
3478 nPos++;
3480 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3481 menu->nItems * sizeof(MENUITEM) );
3483 return TRUE;
3487 /**********************************************************************
3488 * DeleteMenu (USER32.@)
3490 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3492 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3493 if (!item) return FALSE;
3494 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3495 /* nPos is now the position of the item */
3496 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3497 return TRUE;
3501 /*******************************************************************
3502 * ModifyMenuW (USER32.@)
3504 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3505 UINT_PTR id, LPCWSTR str )
3507 MENUITEM *item;
3509 if (IS_STRING_ITEM(flags))
3511 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3513 else
3515 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3518 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3519 return MENU_SetItemData( item, flags, id, str );
3523 /*******************************************************************
3524 * ModifyMenuA (USER32.@)
3526 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3527 UINT_PTR id, LPCSTR str )
3529 BOOL ret = FALSE;
3531 if (IS_STRING_ITEM(flags) && str)
3533 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3534 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3535 if (newstr)
3537 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3538 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3539 HeapFree( GetProcessHeap(), 0, newstr );
3541 return ret;
3543 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3547 /**********************************************************************
3548 * CreatePopupMenu (USER32.@)
3550 HMENU WINAPI CreatePopupMenu(void)
3552 HMENU hmenu;
3553 POPUPMENU *menu;
3555 if (!(hmenu = CreateMenu())) return 0;
3556 menu = MENU_GetMenu( hmenu );
3557 menu->wFlags |= MF_POPUP;
3558 menu->bTimeToHide = FALSE;
3559 return hmenu;
3563 /**********************************************************************
3564 * GetMenuCheckMarkDimensions (USER.417)
3565 * GetMenuCheckMarkDimensions (USER32.@)
3567 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3569 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3573 /**********************************************************************
3574 * SetMenuItemBitmaps (USER32.@)
3576 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3577 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3579 MENUITEM *item;
3580 TRACE("(%p, %04x, %04x, %p, %p)\n",
3581 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3582 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3584 if (!hNewCheck && !hNewUnCheck)
3586 item->fState &= ~MF_USECHECKBITMAPS;
3588 else /* Install new bitmaps */
3590 item->hCheckBit = hNewCheck;
3591 item->hUnCheckBit = hNewUnCheck;
3592 item->fState |= MF_USECHECKBITMAPS;
3594 return TRUE;
3598 /**********************************************************************
3599 * CreateMenu (USER32.@)
3601 HMENU WINAPI CreateMenu(void)
3603 HMENU hMenu;
3604 LPPOPUPMENU menu;
3605 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3606 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3608 ZeroMemory(menu, sizeof(POPUPMENU));
3609 menu->wMagic = MENU_MAGIC;
3610 menu->FocusedItem = NO_SELECTED_ITEM;
3611 menu->bTimeToHide = FALSE;
3613 TRACE("return %p\n", hMenu );
3615 return hMenu;
3619 /**********************************************************************
3620 * DestroyMenu (USER32.@)
3622 BOOL WINAPI DestroyMenu( HMENU hMenu )
3624 TRACE("(%p)\n", hMenu);
3626 /* Silently ignore attempts to destroy default system popup */
3628 if (hMenu && hMenu != MENU_DefSysPopup)
3630 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3632 if (!lppop) return FALSE;
3634 lppop->wMagic = 0; /* Mark it as destroyed */
3636 /* DestroyMenu should not destroy system menu popup owner */
3637 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3639 DestroyWindow( lppop->hWnd );
3640 lppop->hWnd = 0;
3643 if (lppop->items) /* recursively destroy submenus */
3645 int i;
3646 MENUITEM *item = lppop->items;
3647 for (i = lppop->nItems; i > 0; i--, item++)
3649 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3650 MENU_FreeItemData( item );
3652 HeapFree( GetProcessHeap(), 0, lppop->items );
3654 USER_HEAP_FREE( hMenu );
3656 return (hMenu != MENU_DefSysPopup);
3660 /**********************************************************************
3661 * GetSystemMenu (USER32.@)
3663 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3665 WND *wndPtr = WIN_GetPtr( hWnd );
3666 HMENU retvalue = 0;
3668 if (wndPtr == WND_DESKTOP) return 0;
3669 if (wndPtr == WND_OTHER_PROCESS)
3671 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3673 else if (wndPtr)
3675 if( wndPtr->hSysMenu )
3677 if( bRevert )
3679 DestroyMenu(wndPtr->hSysMenu);
3680 wndPtr->hSysMenu = 0;
3682 else
3684 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3685 if( menu )
3687 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3688 menu->items[0].hSubMenu = MENU_CopySysPopup();
3690 else
3692 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3693 wndPtr->hSysMenu, hWnd);
3694 wndPtr->hSysMenu = 0;
3699 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3700 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3702 if( wndPtr->hSysMenu )
3704 POPUPMENU *menu;
3705 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3707 /* Store the dummy sysmenu handle to facilitate the refresh */
3708 /* of the close button if the SC_CLOSE item change */
3709 menu = MENU_GetMenu(retvalue);
3710 if ( menu )
3711 menu->hSysMenuOwner = wndPtr->hSysMenu;
3713 WIN_ReleasePtr( wndPtr );
3715 return bRevert ? 0 : retvalue;
3719 /*******************************************************************
3720 * SetSystemMenu (USER32.@)
3722 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3724 WND *wndPtr = WIN_GetPtr( hwnd );
3726 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3728 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3729 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3730 WIN_ReleasePtr( wndPtr );
3731 return TRUE;
3733 return FALSE;
3737 /**********************************************************************
3738 * GetMenu (USER32.@)
3740 HMENU WINAPI GetMenu( HWND hWnd )
3742 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3743 TRACE("for %p returning %p\n", hWnd, retvalue);
3744 return retvalue;
3748 /**********************************************************************
3749 * MENU_SetMenu
3751 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3752 * SetWindowPos call that would result if SetMenu were called directly.
3754 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3756 TRACE("(%p, %p);\n", hWnd, hMenu);
3758 if (hMenu && !IsMenu(hMenu))
3760 WARN("hMenu %p is not a menu handle\n", hMenu);
3761 return FALSE;
3763 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3764 return FALSE;
3766 hWnd = WIN_GetFullHandle( hWnd );
3767 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3769 if (hMenu != 0)
3771 LPPOPUPMENU lpmenu;
3773 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3775 lpmenu->hWnd = hWnd;
3776 lpmenu->Height = 0; /* Make sure we recalculate the size */
3778 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3779 return TRUE;
3783 /**********************************************************************
3784 * SetMenu (USER32.@)
3786 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3788 if(!MENU_SetMenu(hWnd, hMenu))
3789 return FALSE;
3791 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3792 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3793 return TRUE;
3797 /**********************************************************************
3798 * GetSubMenu (USER32.@)
3800 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3802 MENUITEM * lpmi;
3804 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3805 if (!(lpmi->fType & MF_POPUP)) return 0;
3806 return lpmi->hSubMenu;
3810 /**********************************************************************
3811 * DrawMenuBar (USER32.@)
3813 BOOL WINAPI DrawMenuBar( HWND hWnd )
3815 LPPOPUPMENU lppop;
3816 HMENU hMenu = GetMenu(hWnd);
3818 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3819 return FALSE;
3820 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3822 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3823 lppop->hwndOwner = hWnd;
3824 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3825 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3826 return TRUE;
3829 /***********************************************************************
3830 * DrawMenuBarTemp (USER32.@)
3832 * UNDOCUMENTED !!
3834 * called by W98SE desk.cpl Control Panel Applet
3836 * Not 100% sure about the param names, but close.
3838 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3840 LPPOPUPMENU lppop;
3841 UINT i,retvalue;
3842 HFONT hfontOld = 0;
3844 if (!hMenu)
3845 hMenu = GetMenu(hwnd);
3847 if (!hFont)
3848 hFont = hMenuFont;
3850 lppop = MENU_GetMenu( hMenu );
3851 if (lppop == NULL || lprect == NULL)
3853 retvalue = GetSystemMetrics(SM_CYMENU);
3854 goto END;
3857 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3859 hfontOld = SelectObject( hDC, hFont);
3861 if (lppop->Height == 0)
3862 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3864 lprect->bottom = lprect->top + lppop->Height;
3866 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3868 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3869 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3870 LineTo( hDC, lprect->right, lprect->bottom );
3872 if (lppop->nItems == 0)
3874 retvalue = GetSystemMetrics(SM_CYMENU);
3875 goto END;
3878 for (i = 0; i < lppop->nItems; i++)
3880 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3881 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3883 retvalue = lppop->Height;
3885 END:
3886 if (hfontOld) SelectObject (hDC, hfontOld);
3887 return retvalue;
3890 /***********************************************************************
3891 * EndMenu (USER.187)
3892 * EndMenu (USER32.@)
3894 void WINAPI EndMenu(void)
3896 /* if we are in the menu code, and it is active */
3897 if (!fEndMenu && top_popup)
3899 /* terminate the menu handling code */
3900 fEndMenu = TRUE;
3902 /* needs to be posted to wakeup the internal menu handler */
3903 /* which will now terminate the menu, in the event that */
3904 /* the main window was minimized, or lost focus, so we */
3905 /* don't end up with an orphaned menu */
3906 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3911 /***********************************************************************
3912 * LookupMenuHandle (USER.217)
3914 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3916 HMENU hmenu32 = HMENU_32(hmenu);
3917 UINT id32 = id;
3918 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3919 else return HMENU_16(hmenu32);
3923 /**********************************************************************
3924 * LoadMenu (USER.150)
3926 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3928 HRSRC16 hRsrc;
3929 HGLOBAL16 handle;
3930 HMENU16 hMenu;
3932 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3933 if (!name) return 0;
3935 instance = GetExePtr( instance );
3936 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3937 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3938 hMenu = LoadMenuIndirect16(LockResource16(handle));
3939 FreeResource16( handle );
3940 return hMenu;
3944 /*****************************************************************
3945 * LoadMenuA (USER32.@)
3947 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3949 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3950 if (!hrsrc) return 0;
3951 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3955 /*****************************************************************
3956 * LoadMenuW (USER32.@)
3958 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3960 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3961 if (!hrsrc) return 0;
3962 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3966 /**********************************************************************
3967 * LoadMenuIndirect (USER.220)
3969 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3971 HMENU hMenu;
3972 WORD version, offset;
3973 LPCSTR p = (LPCSTR)template;
3975 TRACE("(%p)\n", template );
3976 version = GET_WORD(p);
3977 p += sizeof(WORD);
3978 if (version)
3980 WARN("version must be 0 for Win16\n" );
3981 return 0;
3983 offset = GET_WORD(p);
3984 p += sizeof(WORD) + offset;
3985 if (!(hMenu = CreateMenu())) return 0;
3986 if (!MENU_ParseResource( p, hMenu, FALSE ))
3988 DestroyMenu( hMenu );
3989 return 0;
3991 return HMENU_16(hMenu);
3995 /**********************************************************************
3996 * LoadMenuIndirectW (USER32.@)
3998 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4000 HMENU hMenu;
4001 WORD version, offset;
4002 LPCSTR p = (LPCSTR)template;
4004 version = GET_WORD(p);
4005 p += sizeof(WORD);
4006 TRACE("%p, ver %d\n", template, version );
4007 switch (version)
4009 case 0: /* standard format is version of 0 */
4010 offset = GET_WORD(p);
4011 p += sizeof(WORD) + offset;
4012 if (!(hMenu = CreateMenu())) return 0;
4013 if (!MENU_ParseResource( p, hMenu, TRUE ))
4015 DestroyMenu( hMenu );
4016 return 0;
4018 return hMenu;
4019 case 1: /* extended format is version of 1 */
4020 offset = GET_WORD(p);
4021 p += sizeof(WORD) + offset;
4022 if (!(hMenu = CreateMenu())) return 0;
4023 if (!MENUEX_ParseResource( p, hMenu))
4025 DestroyMenu( hMenu );
4026 return 0;
4028 return hMenu;
4029 default:
4030 ERR("version %d not supported.\n", version);
4031 return 0;
4036 /**********************************************************************
4037 * LoadMenuIndirectA (USER32.@)
4039 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4041 return LoadMenuIndirectW( template );
4045 /**********************************************************************
4046 * IsMenu (USER32.@)
4048 BOOL WINAPI IsMenu(HMENU hmenu)
4050 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4051 return menu != NULL;
4054 /**********************************************************************
4055 * GetMenuItemInfo_common
4058 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4059 LPMENUITEMINFOW lpmii, BOOL unicode)
4061 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4063 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4065 if (!menu)
4066 return FALSE;
4068 if (lpmii->fMask & MIIM_TYPE) {
4069 lpmii->fType = menu->fType;
4070 switch (MENU_ITEM_TYPE(menu->fType)) {
4071 case MF_STRING:
4072 break; /* will be done below */
4073 case MF_OWNERDRAW:
4074 case MF_BITMAP:
4075 lpmii->dwTypeData = menu->text;
4076 /* fall through */
4077 default:
4078 lpmii->cch = 0;
4082 /* copy the text string */
4083 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4084 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4086 int len;
4087 if (unicode)
4089 len = strlenW(menu->text);
4090 if(lpmii->dwTypeData && lpmii->cch)
4091 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4093 else
4095 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4096 if(lpmii->dwTypeData && lpmii->cch)
4097 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4098 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4099 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4101 /* if we've copied a substring we return its length */
4102 if(lpmii->dwTypeData && lpmii->cch)
4104 if (lpmii->cch <= len) lpmii->cch--;
4106 else /* return length of string */
4107 lpmii->cch = len;
4110 if (lpmii->fMask & MIIM_FTYPE)
4111 lpmii->fType = menu->fType;
4113 if (lpmii->fMask & MIIM_BITMAP)
4114 lpmii->hbmpItem = menu->hbmpItem;
4116 if (lpmii->fMask & MIIM_STATE)
4117 lpmii->fState = menu->fState;
4119 if (lpmii->fMask & MIIM_ID)
4120 lpmii->wID = menu->wID;
4122 if (lpmii->fMask & MIIM_SUBMENU)
4123 lpmii->hSubMenu = menu->hSubMenu;
4125 if (lpmii->fMask & MIIM_CHECKMARKS) {
4126 lpmii->hbmpChecked = menu->hCheckBit;
4127 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4129 if (lpmii->fMask & MIIM_DATA)
4130 lpmii->dwItemData = menu->dwItemData;
4132 return TRUE;
4135 /**********************************************************************
4136 * GetMenuItemInfoA (USER32.@)
4138 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4139 LPMENUITEMINFOA lpmii)
4141 return GetMenuItemInfo_common (hmenu, item, bypos,
4142 (LPMENUITEMINFOW)lpmii, FALSE);
4145 /**********************************************************************
4146 * GetMenuItemInfoW (USER32.@)
4148 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4149 LPMENUITEMINFOW lpmii)
4151 return GetMenuItemInfo_common (hmenu, item, bypos,
4152 lpmii, TRUE);
4156 /* set a menu item text from a ASCII or Unicode string */
4157 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4159 if (!text)
4161 menu->text = NULL;
4162 menu->fType |= MF_SEPARATOR;
4164 else if (unicode)
4166 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4167 strcpyW( menu->text, text );
4169 else
4171 LPCSTR str = (LPCSTR)text;
4172 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4173 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4174 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4179 /**********************************************************************
4180 * SetMenuItemInfo_common
4183 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4184 const MENUITEMINFOW *lpmii,
4185 BOOL unicode)
4187 if (!menu) return FALSE;
4189 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4191 if (lpmii->fMask & MIIM_TYPE ) {
4192 /* Get rid of old string. */
4193 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4194 HeapFree(GetProcessHeap(), 0, menu->text);
4195 menu->text = NULL;
4198 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4199 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4200 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4202 menu->text = lpmii->dwTypeData;
4204 if (IS_STRING_ITEM(menu->fType))
4205 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4208 if (lpmii->fMask & MIIM_FTYPE ) {
4209 /* free the string when the type is changing */
4210 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4211 HeapFree(GetProcessHeap(), 0, menu->text);
4212 menu->text = NULL;
4214 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4215 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4216 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4217 menu->fType |= MF_SEPARATOR;
4220 if (lpmii->fMask & MIIM_STRING ) {
4221 if (IS_STRING_ITEM(menu->fType)) {
4222 /* free the string when used */
4223 HeapFree(GetProcessHeap(), 0, menu->text);
4224 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4228 if (lpmii->fMask & MIIM_STATE)
4230 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4231 menu->fState = lpmii->fState;
4234 if (lpmii->fMask & MIIM_ID)
4235 menu->wID = lpmii->wID;
4237 if (lpmii->fMask & MIIM_SUBMENU) {
4238 menu->hSubMenu = lpmii->hSubMenu;
4239 if (menu->hSubMenu) {
4240 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4241 if (subMenu) {
4242 subMenu->wFlags |= MF_POPUP;
4243 menu->fType |= MF_POPUP;
4245 else
4246 /* FIXME: Return an error ? */
4247 menu->fType &= ~MF_POPUP;
4249 else
4250 menu->fType &= ~MF_POPUP;
4253 if (lpmii->fMask & MIIM_CHECKMARKS)
4255 if (lpmii->fType & MFT_RADIOCHECK)
4256 menu->fType |= MFT_RADIOCHECK;
4258 menu->hCheckBit = lpmii->hbmpChecked;
4259 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4261 if (lpmii->fMask & MIIM_DATA)
4262 menu->dwItemData = lpmii->dwItemData;
4264 if (lpmii->fMask & MIIM_BITMAP)
4265 menu->hbmpItem = lpmii->hbmpItem;
4267 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4268 return TRUE;
4271 /**********************************************************************
4272 * SetMenuItemInfoA (USER32.@)
4274 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4275 const MENUITEMINFOA *lpmii)
4277 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4278 (const MENUITEMINFOW *)lpmii, FALSE);
4281 /**********************************************************************
4282 * SetMenuItemInfoW (USER32.@)
4284 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4285 const MENUITEMINFOW *lpmii)
4287 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4288 lpmii, TRUE);
4291 /**********************************************************************
4292 * SetMenuDefaultItem (USER32.@)
4295 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4297 UINT i;
4298 POPUPMENU *menu;
4299 MENUITEM *item;
4301 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4303 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4305 /* reset all default-item flags */
4306 item = menu->items;
4307 for (i = 0; i < menu->nItems; i++, item++)
4309 item->fState &= ~MFS_DEFAULT;
4312 /* no default item */
4313 if ( -1 == uItem)
4315 return TRUE;
4318 item = menu->items;
4319 if ( bypos )
4321 if ( uItem >= menu->nItems ) return FALSE;
4322 item[uItem].fState |= MFS_DEFAULT;
4323 return TRUE;
4325 else
4327 for (i = 0; i < menu->nItems; i++, item++)
4329 if (item->wID == uItem)
4331 item->fState |= MFS_DEFAULT;
4332 return TRUE;
4337 return FALSE;
4340 /**********************************************************************
4341 * GetMenuDefaultItem (USER32.@)
4343 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4345 POPUPMENU *menu;
4346 MENUITEM * item;
4347 UINT i = 0;
4349 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4351 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4353 /* find default item */
4354 item = menu->items;
4356 /* empty menu */
4357 if (! item) return -1;
4359 while ( !( item->fState & MFS_DEFAULT ) )
4361 i++; item++;
4362 if (i >= menu->nItems ) return -1;
4365 /* default: don't return disabled items */
4366 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4368 /* search rekursiv when needed */
4369 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4371 UINT ret;
4372 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4373 if ( -1 != ret ) return ret;
4375 /* when item not found in submenu, return the popup item */
4377 return ( bypos ) ? i : item->wID;
4382 /**********************************************************************
4383 * InsertMenuItemA (USER32.@)
4385 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4386 const MENUITEMINFOA *lpmii)
4388 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4389 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4393 /**********************************************************************
4394 * InsertMenuItemW (USER32.@)
4396 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4397 const MENUITEMINFOW *lpmii)
4399 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4400 return SetMenuItemInfo_common(item, lpmii, TRUE);
4403 /**********************************************************************
4404 * CheckMenuRadioItem (USER32.@)
4407 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4408 UINT first, UINT last, UINT check,
4409 UINT bypos)
4411 MENUITEM *mifirst, *milast, *micheck;
4412 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4414 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4416 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4417 milast = MENU_FindItem (&mlast, &last, bypos);
4418 micheck = MENU_FindItem (&mcheck, &check, bypos);
4420 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4421 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4422 micheck > milast || micheck < mifirst)
4423 return FALSE;
4425 while (mifirst <= milast)
4427 if (mifirst == micheck)
4429 mifirst->fType |= MFT_RADIOCHECK;
4430 mifirst->fState |= MFS_CHECKED;
4431 } else {
4432 mifirst->fType &= ~MFT_RADIOCHECK;
4433 mifirst->fState &= ~MFS_CHECKED;
4435 mifirst++;
4438 return TRUE;
4442 /**********************************************************************
4443 * GetMenuItemRect (USER32.@)
4445 * ATTENTION: Here, the returned values in rect are the screen
4446 * coordinates of the item just like if the menu was
4447 * always on the upper left side of the application.
4450 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4451 LPRECT rect)
4453 POPUPMENU *itemMenu;
4454 MENUITEM *item;
4455 HWND referenceHwnd;
4457 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4459 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4460 referenceHwnd = hwnd;
4462 if(!hwnd)
4464 itemMenu = MENU_GetMenu(hMenu);
4465 if (itemMenu == NULL)
4466 return FALSE;
4468 if(itemMenu->hWnd == 0)
4469 return FALSE;
4470 referenceHwnd = itemMenu->hWnd;
4473 if ((rect == NULL) || (item == NULL))
4474 return FALSE;
4476 *rect = item->rect;
4478 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4480 return TRUE;
4484 /**********************************************************************
4485 * SetMenuInfo (USER32.@)
4487 * FIXME
4488 * MIM_APPLYTOSUBMENUS
4489 * actually use the items to draw the menu
4491 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4493 POPUPMENU *menu;
4495 TRACE("(%p %p)\n", hMenu, lpmi);
4497 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4500 if (lpmi->fMask & MIM_BACKGROUND)
4501 menu->hbrBack = lpmi->hbrBack;
4503 if (lpmi->fMask & MIM_HELPID)
4504 menu->dwContextHelpID = lpmi->dwContextHelpID;
4506 if (lpmi->fMask & MIM_MAXHEIGHT)
4507 menu->cyMax = lpmi->cyMax;
4509 if (lpmi->fMask & MIM_MENUDATA)
4510 menu->dwMenuData = lpmi->dwMenuData;
4512 if (lpmi->fMask & MIM_STYLE)
4514 menu->dwStyle = lpmi->dwStyle;
4515 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4516 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4517 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4518 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4519 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4522 return TRUE;
4524 return FALSE;
4527 /**********************************************************************
4528 * GetMenuInfo (USER32.@)
4530 * NOTES
4531 * win98/NT5.0
4534 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4535 { POPUPMENU *menu;
4537 TRACE("(%p %p)\n", hMenu, lpmi);
4539 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4542 if (lpmi->fMask & MIM_BACKGROUND)
4543 lpmi->hbrBack = menu->hbrBack;
4545 if (lpmi->fMask & MIM_HELPID)
4546 lpmi->dwContextHelpID = menu->dwContextHelpID;
4548 if (lpmi->fMask & MIM_MAXHEIGHT)
4549 lpmi->cyMax = menu->cyMax;
4551 if (lpmi->fMask & MIM_MENUDATA)
4552 lpmi->dwMenuData = menu->dwMenuData;
4554 if (lpmi->fMask & MIM_STYLE)
4555 lpmi->dwStyle = menu->dwStyle;
4557 return TRUE;
4559 return FALSE;
4563 /**********************************************************************
4564 * SetMenuContextHelpId (USER32.@)
4566 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4568 LPPOPUPMENU menu;
4570 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4572 if ((menu = MENU_GetMenu(hMenu)))
4574 menu->dwContextHelpID = dwContextHelpID;
4575 return TRUE;
4577 return FALSE;
4581 /**********************************************************************
4582 * GetMenuContextHelpId (USER32.@)
4584 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4586 LPPOPUPMENU menu;
4588 TRACE("(%p)\n", hMenu);
4590 if ((menu = MENU_GetMenu(hMenu)))
4592 return menu->dwContextHelpID;
4594 return 0;
4597 /**********************************************************************
4598 * MenuItemFromPoint (USER32.@)
4600 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4602 POPUPMENU *menu = MENU_GetMenu(hMenu);
4603 UINT pos;
4605 /*FIXME: Do we have to handle hWnd here? */
4606 if (!menu) return -1;
4607 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4608 return pos;
4612 /**********************************************************************
4613 * translate_accelerator
4615 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4616 BYTE fVirt, WORD key, WORD cmd )
4618 INT mask = 0;
4619 UINT mesg = 0;
4621 if (wParam != key) return FALSE;
4623 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4624 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4625 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4627 if (message == WM_CHAR || message == WM_SYSCHAR)
4629 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4631 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4632 goto found;
4635 else
4637 if(fVirt & FVIRTKEY)
4639 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4640 wParam, 0xff & HIWORD(lParam));
4642 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4643 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4645 else
4647 if (!(lParam & 0x01000000)) /* no special_key */
4649 if ((fVirt & FALT) && (lParam & 0x20000000))
4650 { /* ^^ ALT pressed */
4651 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4652 goto found;
4657 return FALSE;
4659 found:
4660 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4661 mesg = 1;
4662 else
4664 HMENU hMenu, hSubMenu, hSysMenu;
4665 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4667 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4668 hSysMenu = get_win_sys_menu( hWnd );
4670 /* find menu item and ask application to initialize it */
4671 /* 1. in the system menu */
4672 hSubMenu = hSysMenu;
4673 nPos = cmd;
4674 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4676 if (GetCapture())
4677 mesg = 2;
4678 if (!IsWindowEnabled(hWnd))
4679 mesg = 3;
4680 else
4682 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4683 if(hSubMenu != hSysMenu)
4685 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4686 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4687 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4689 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4692 else /* 2. in the window's menu */
4694 hSubMenu = hMenu;
4695 nPos = cmd;
4696 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4698 if (GetCapture())
4699 mesg = 2;
4700 if (!IsWindowEnabled(hWnd))
4701 mesg = 3;
4702 else
4704 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4705 if(hSubMenu != hMenu)
4707 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4708 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4709 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4711 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4716 if (mesg == 0)
4718 if (uSysStat != (UINT)-1)
4720 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4721 mesg=4;
4722 else
4723 mesg=WM_SYSCOMMAND;
4725 else
4727 if (uStat != (UINT)-1)
4729 if (IsIconic(hWnd))
4730 mesg=5;
4731 else
4733 if (uStat & (MF_DISABLED|MF_GRAYED))
4734 mesg=6;
4735 else
4736 mesg=WM_COMMAND;
4739 else
4740 mesg=WM_COMMAND;
4745 if( mesg==WM_COMMAND )
4747 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4748 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4750 else if( mesg==WM_SYSCOMMAND )
4752 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4753 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4755 else
4757 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4758 * #0: unknown (please report!)
4759 * #1: for WM_KEYUP,WM_SYSKEYUP
4760 * #2: mouse is captured
4761 * #3: window is disabled
4762 * #4: it's a disabled system menu option
4763 * #5: it's a menu option, but window is iconic
4764 * #6: it's a menu option, but disabled
4766 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4767 if(mesg==0)
4768 ERR_(accel)(" unknown reason - please report!\n");
4770 return TRUE;
4773 /**********************************************************************
4774 * TranslateAccelerator (USER32.@)
4775 * TranslateAcceleratorA (USER32.@)
4777 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4779 /* YES, Accel16! */
4780 LPACCEL16 lpAccelTbl;
4781 int i;
4782 WPARAM wParam;
4784 if (!hWnd || !msg) return 0;
4786 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4788 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4789 return 0;
4792 wParam = msg->wParam;
4794 switch (msg->message)
4796 case WM_KEYDOWN:
4797 case WM_SYSKEYDOWN:
4798 break;
4800 case WM_CHAR:
4801 case WM_SYSCHAR:
4803 char ch = LOWORD(wParam);
4804 WCHAR wch;
4805 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4806 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4808 break;
4810 default:
4811 return 0;
4814 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4815 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4816 i = 0;
4819 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4820 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4821 return 1;
4822 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4824 return 0;
4827 /**********************************************************************
4828 * TranslateAcceleratorW (USER32.@)
4830 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4832 /* YES, Accel16! */
4833 LPACCEL16 lpAccelTbl;
4834 int i;
4836 if (!hWnd || !msg) return 0;
4838 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4840 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4841 return 0;
4844 switch (msg->message)
4846 case WM_KEYDOWN:
4847 case WM_SYSKEYDOWN:
4848 case WM_CHAR:
4849 case WM_SYSCHAR:
4850 break;
4852 default:
4853 return 0;
4856 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4857 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4858 i = 0;
4861 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4862 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4863 return 1;
4864 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4866 return 0;