- Conversions between variants types of the same size should ignore
[wine/multimedia.git] / dlls / user / menu.c
blobf5396ef6efaf4d81cd1151ece1f8cca4826a2853
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 /* Space between 2 columns */
146 #define MENU_COL_SPACE 4
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
155 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
156 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
162 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
163 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
164 MF_POPUP | MF_SYSMENU | MF_HELP)
165 #define STATE_MASK (~TYPE_MASK)
167 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
169 /* Dimension of the menu bitmaps */
170 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
172 static HBITMAP hStdMnArrow = 0;
173 static HBITMAP hBmpSysMenu = 0;
175 static HBRUSH hShadeBrush = 0;
176 static HFONT hMenuFont = 0;
177 static HFONT hMenuFontBold = 0;
178 static SIZE menucharsize;
179 static UINT ODitemheight; /* default owner drawn item height */
181 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
183 /* Use global popup window because there's no way 2 menus can
184 * be tracked at the same time. */
185 static HWND top_popup;
187 /* Flag set by EndMenu() to force an exit from menu tracking */
188 static BOOL fEndMenu = FALSE;
190 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
192 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
194 /*********************************************************************
195 * menu class descriptor
197 const struct builtin_class_descr MENU_builtin_class =
199 POPUPMENU_CLASS_ATOMA, /* name */
200 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
201 NULL, /* procA (winproc is Unicode only) */
202 PopupMenuWndProc, /* procW */
203 sizeof(HMENU), /* extra */
204 IDC_ARROW, /* cursor */
205 (HBRUSH)(COLOR_MENU+1) /* brush */
209 /***********************************************************************
210 * debug_print_menuitem
212 * Print a menuitem in readable form.
215 #define debug_print_menuitem(pre, mp, post) \
216 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
218 #define MENUOUT(text) \
219 DPRINTF("%s%s", (count++ ? "," : ""), (text))
221 #define MENUFLAG(bit,text) \
222 do { \
223 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
224 } while (0)
226 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
227 const char *postfix)
229 TRACE("%s ", prefix);
230 if (mp) {
231 UINT flags = mp->fType;
232 int type = MENU_ITEM_TYPE(flags);
233 DPRINTF( "{ ID=0x%x", mp->wID);
234 if (flags & MF_POPUP)
235 DPRINTF( ", Sub=%p", mp->hSubMenu);
236 if (flags) {
237 int count = 0;
238 DPRINTF( ", Type=");
239 if (type == MFT_STRING)
240 /* Nothing */ ;
241 else if (type == MFT_SEPARATOR)
242 MENUOUT("sep");
243 else if (type == MFT_OWNERDRAW)
244 MENUOUT("own");
245 else if (type == MFT_BITMAP)
246 MENUOUT("bit");
247 else
248 MENUOUT("???");
249 flags -= type;
251 MENUFLAG(MF_POPUP, "pop");
252 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
253 MENUFLAG(MFT_MENUBREAK, "brk");
254 MENUFLAG(MFT_RADIOCHECK, "radio");
255 MENUFLAG(MFT_RIGHTORDER, "rorder");
256 MENUFLAG(MF_SYSMENU, "sys");
257 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
259 if (flags)
260 DPRINTF( "+0x%x", flags);
262 flags = mp->fState;
263 if (flags) {
264 int count = 0;
265 DPRINTF( ", State=");
266 MENUFLAG(MFS_GRAYED, "grey");
267 MENUFLAG(MFS_DEFAULT, "default");
268 MENUFLAG(MFS_DISABLED, "dis");
269 MENUFLAG(MFS_CHECKED, "check");
270 MENUFLAG(MFS_HILITE, "hi");
271 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
272 MENUFLAG(MF_MOUSESELECT, "mouse");
273 if (flags)
274 DPRINTF( "+0x%x", flags);
276 if (mp->hCheckBit)
277 DPRINTF( ", Chk=%p", mp->hCheckBit);
278 if (mp->hUnCheckBit)
279 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
281 if (type == MFT_STRING) {
282 if (mp->text)
283 DPRINTF( ", Text=%s", debugstr_w(mp->text));
284 else
285 DPRINTF( ", Text=Null");
286 } else if (mp->text == NULL)
287 /* Nothing */ ;
288 else
289 DPRINTF( ", Text=%p", mp->text);
290 if (mp->dwItemData)
291 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
292 DPRINTF( " }");
293 } else {
294 DPRINTF( "NULL");
297 DPRINTF(" %s\n", postfix);
300 #undef MENUOUT
301 #undef MENUFLAG
304 /***********************************************************************
305 * MENU_GetMenu
307 * Validate the given menu handle and returns the menu structure pointer.
309 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
311 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
312 if (!menu || menu->wMagic != MENU_MAGIC)
314 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
315 menu = NULL;
317 return menu;
320 /***********************************************************************
321 * get_win_sys_menu
323 * Get the system menu of a window
325 static HMENU get_win_sys_menu( HWND hwnd )
327 HMENU ret = 0;
328 WND *win = WIN_GetPtr( hwnd );
329 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
331 ret = win->hSysMenu;
332 WIN_ReleasePtr( win );
334 return ret;
337 /***********************************************************************
338 * MENU_CopySysPopup
340 * Return the default system menu.
342 static HMENU MENU_CopySysPopup(void)
344 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
345 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
347 if( hMenu ) {
348 POPUPMENU* menu = MENU_GetMenu(hMenu);
349 menu->wFlags |= MF_SYSMENU | MF_POPUP;
350 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
352 else
353 ERR("Unable to load default system menu\n" );
355 TRACE("returning %p.\n", hMenu );
357 return hMenu;
361 /**********************************************************************
362 * MENU_GetSysMenu
364 * Create a copy of the system menu. System menu in Windows is
365 * a special menu bar with the single entry - system menu popup.
366 * This popup is presented to the outside world as a "system menu".
367 * However, the real system menu handle is sometimes seen in the
368 * WM_MENUSELECT parameters (and Word 6 likes it this way).
370 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
372 HMENU hMenu;
374 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
375 if ((hMenu = CreateMenu()))
377 POPUPMENU *menu = MENU_GetMenu(hMenu);
378 menu->wFlags = MF_SYSMENU;
379 menu->hWnd = WIN_GetFullHandle( hWnd );
380 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
382 if (hPopupMenu == (HMENU)(-1))
383 hPopupMenu = MENU_CopySysPopup();
384 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
386 if (hPopupMenu)
388 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
389 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
391 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
392 (UINT_PTR)hPopupMenu, NULL );
394 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
395 menu->items[0].fState = 0;
396 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
398 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
399 return hMenu;
401 DestroyMenu( hMenu );
403 ERR("failed to load system menu!\n");
404 return 0;
408 /***********************************************************************
409 * MENU_Init
411 * Menus initialisation.
413 BOOL MENU_Init(void)
415 HBITMAP hBitmap;
416 NONCLIENTMETRICSW ncm;
418 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
419 0x55, 0, 0xAA, 0,
420 0x55, 0, 0xAA, 0,
421 0x55, 0, 0xAA, 0 };
423 /* Load menu bitmaps */
424 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
425 /* Load system buttons bitmaps */
426 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
428 if (hStdMnArrow)
430 BITMAP bm;
431 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
432 arrow_bitmap_width = bm.bmWidth;
433 arrow_bitmap_height = bm.bmHeight;
434 } else
435 return FALSE;
437 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
438 return FALSE;
440 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
441 return FALSE;
443 DeleteObject( hBitmap );
444 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
445 return FALSE;
447 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
448 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
449 return FALSE;
451 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
452 return FALSE;
454 ncm.lfMenuFont.lfWeight += 300;
455 if ( ncm.lfMenuFont.lfWeight > 1000)
456 ncm.lfMenuFont.lfWeight = 1000;
458 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
459 return FALSE;
461 return TRUE;
464 /***********************************************************************
465 * MENU_InitSysMenuPopup
467 * Grey the appropriate items in System menu.
469 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
471 BOOL gray;
473 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
474 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
475 gray = ((style & WS_MAXIMIZE) != 0);
476 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
477 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
478 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
479 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
480 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
481 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
482 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
483 gray = (clsStyle & CS_NOCLOSE) != 0;
485 /* The menu item must keep its state if it's disabled */
486 if(gray)
487 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
491 /******************************************************************************
493 * UINT MENU_GetStartOfNextColumn(
494 * HMENU hMenu )
496 *****************************************************************************/
498 static UINT MENU_GetStartOfNextColumn(
499 HMENU hMenu )
501 POPUPMENU *menu = MENU_GetMenu(hMenu);
502 UINT i;
504 if(!menu)
505 return NO_SELECTED_ITEM;
507 i = menu->FocusedItem + 1;
508 if( i == NO_SELECTED_ITEM )
509 return i;
511 for( ; i < menu->nItems; ++i ) {
512 if (menu->items[i].fType & MF_MENUBARBREAK)
513 return i;
516 return NO_SELECTED_ITEM;
520 /******************************************************************************
522 * UINT MENU_GetStartOfPrevColumn(
523 * HMENU hMenu )
525 *****************************************************************************/
527 static UINT MENU_GetStartOfPrevColumn(
528 HMENU hMenu )
530 POPUPMENU *menu = MENU_GetMenu(hMenu);
531 UINT i;
533 if( !menu )
534 return NO_SELECTED_ITEM;
536 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
537 return NO_SELECTED_ITEM;
539 /* Find the start of the column */
541 for(i = menu->FocusedItem; i != 0 &&
542 !(menu->items[i].fType & MF_MENUBARBREAK);
543 --i); /* empty */
545 if(i == 0)
546 return NO_SELECTED_ITEM;
548 for(--i; i != 0; --i) {
549 if (menu->items[i].fType & MF_MENUBARBREAK)
550 break;
553 TRACE("ret %d.\n", i );
555 return i;
560 /***********************************************************************
561 * MENU_FindItem
563 * Find a menu item. Return a pointer on the item, and modifies *hmenu
564 * in case the item was in a sub-menu.
566 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
568 POPUPMENU *menu;
569 UINT i;
571 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
572 if (wFlags & MF_BYPOSITION)
574 if (*nPos >= menu->nItems) return NULL;
575 return &menu->items[*nPos];
577 else
579 MENUITEM *item = menu->items;
580 for (i = 0; i < menu->nItems; i++, item++)
582 if (item->wID == *nPos)
584 *nPos = i;
585 return item;
587 else if (item->fType & MF_POPUP)
589 HMENU hsubmenu = item->hSubMenu;
590 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
591 if (subitem)
593 *hmenu = hsubmenu;
594 return subitem;
599 return NULL;
602 /***********************************************************************
603 * MENU_FindSubMenu
605 * Find a Sub menu. Return the position of the submenu, and modifies
606 * *hmenu in case it is found in another sub-menu.
607 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
609 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
611 POPUPMENU *menu;
612 UINT i;
613 MENUITEM *item;
614 if (((*hmenu)==(HMENU)0xffff) ||
615 (!(menu = MENU_GetMenu(*hmenu))))
616 return NO_SELECTED_ITEM;
617 item = menu->items;
618 for (i = 0; i < menu->nItems; i++, item++) {
619 if(!(item->fType & MF_POPUP)) continue;
620 if (item->hSubMenu == hSubTarget) {
621 return i;
623 else {
624 HMENU hsubmenu = item->hSubMenu;
625 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
626 if (pos != NO_SELECTED_ITEM) {
627 *hmenu = hsubmenu;
628 return pos;
632 return NO_SELECTED_ITEM;
635 /***********************************************************************
636 * MENU_FreeItemData
638 static void MENU_FreeItemData( MENUITEM* item )
640 /* delete text */
641 if (IS_STRING_ITEM(item->fType) && item->text)
642 HeapFree( GetProcessHeap(), 0, item->text );
645 /***********************************************************************
646 * MENU_FindItemByCoords
648 * Find the item at the specified coordinates (screen coords). Does
649 * not work for child windows and therefore should not be called for
650 * an arbitrary system menu.
652 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
653 POINT pt, UINT *pos )
655 MENUITEM *item;
656 UINT i;
657 RECT wrect;
659 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
660 pt.x -= wrect.left;pt.y -= wrect.top;
661 item = menu->items;
662 for (i = 0; i < menu->nItems; i++, item++)
664 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
665 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
667 if (pos) *pos = i;
668 return item;
671 return NULL;
675 /***********************************************************************
676 * MENU_FindItemByKey
678 * Find the menu item selected by a key press.
679 * Return item id, -1 if none, -2 if we should close the menu.
681 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
682 WCHAR key, BOOL forceMenuChar )
684 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
686 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
688 if (hmenu)
690 POPUPMENU *menu = MENU_GetMenu( hmenu );
691 MENUITEM *item = menu->items;
692 LRESULT menuchar;
694 if( !forceMenuChar )
696 UINT i;
698 for (i = 0; i < menu->nItems; i++, item++)
700 if (IS_STRING_ITEM(item->fType) && item->text)
702 WCHAR *p = item->text - 2;
705 p = strchrW (p + 2, '&');
707 while (p != NULL && p [1] == '&');
708 if (p && (toupperW(p[1]) == toupperW(key))) return i;
712 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
713 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
714 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
715 if (HIWORD(menuchar) == 1) return (UINT)(-2);
717 return (UINT)(-1);
721 /***********************************************************************
722 * MENU_GetBitmapItemSize
724 * Get the size of a bitmap item.
726 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
728 BITMAP bm;
729 HBITMAP bmp = (HBITMAP)id;
731 size->cx = size->cy = 0;
733 /* check if there is a magic menu item associated with this item */
734 if (id && IS_MAGIC_ITEM( id ))
736 switch(LOWORD(id))
738 case (INT_PTR)HBMMENU_SYSTEM:
739 if (data)
741 bmp = (HBITMAP)data;
742 break;
744 /* fall through */
745 case (INT_PTR)HBMMENU_MBAR_RESTORE:
746 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
747 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
748 case (INT_PTR)HBMMENU_MBAR_CLOSE:
749 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
750 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
751 size->cy = size->cx;
752 return;
753 case (INT_PTR)HBMMENU_CALLBACK:
754 case (INT_PTR)HBMMENU_POPUP_CLOSE:
755 case (INT_PTR)HBMMENU_POPUP_RESTORE:
756 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
757 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
758 default:
759 FIXME("Magic 0x%08x not implemented\n", id);
760 return;
763 if (GetObjectW(bmp, sizeof(bm), &bm ))
765 size->cx = bm.bmWidth;
766 size->cy = bm.bmHeight;
770 /***********************************************************************
771 * MENU_DrawBitmapItem
773 * Draw a bitmap item.
774 * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
776 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
778 BITMAP bm;
779 DWORD rop;
780 HDC hdcMem;
781 HBITMAP bmp = (HBITMAP)lpitem->text;
782 int w = rect->right - rect->left;
783 int h = rect->bottom - rect->top;
784 int bmp_xoffset = 0;
785 int left, top;
786 HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;
788 /* Check if there is a magic menu item associated with this item */
789 if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
791 UINT flags = 0;
792 RECT r;
794 switch(LOWORD(hbmToDraw))
796 case (INT_PTR)HBMMENU_SYSTEM:
797 if (lpitem->dwItemData)
799 bmp = (HBITMAP)lpitem->dwItemData;
800 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
802 else
804 bmp = hBmpSysMenu;
805 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
806 /* only use right half of the bitmap */
807 bmp_xoffset = bm.bmWidth / 2;
808 bm.bmWidth -= bmp_xoffset;
810 goto got_bitmap;
811 case (INT_PTR)HBMMENU_MBAR_RESTORE:
812 flags = DFCS_CAPTIONRESTORE;
813 break;
814 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
815 flags = DFCS_CAPTIONMIN;
816 break;
817 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
818 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
819 break;
820 case (INT_PTR)HBMMENU_MBAR_CLOSE:
821 flags = DFCS_CAPTIONCLOSE;
822 break;
823 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
824 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
825 break;
826 case (INT_PTR)HBMMENU_CALLBACK:
827 case (INT_PTR)HBMMENU_POPUP_CLOSE:
828 case (INT_PTR)HBMMENU_POPUP_RESTORE:
829 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
830 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
831 default:
832 FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw));
833 return;
835 r = *rect;
836 InflateRect( &r, -1, -1 );
837 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
838 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
839 return;
842 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
844 got_bitmap:
845 hdcMem = CreateCompatibleDC( hdc );
846 SelectObject( hdcMem, bmp );
848 /* handle fontsize > bitmap_height */
849 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
850 left=rect->left;
851 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
852 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
853 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
854 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
855 DeleteDC( hdcMem );
859 /***********************************************************************
860 * MENU_CalcItemSize
862 * Calculate the size of the menu item and store it in lpitem->rect.
864 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
865 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
867 WCHAR *p;
868 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
870 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
871 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
872 (menuBar ? " (MenuBar)" : ""));
874 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
876 if (lpitem->fType & MF_OWNERDRAW)
878 MEASUREITEMSTRUCT mis;
879 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
880 if( !menucharsize.cx ) {
881 DIALOG_GetCharSize( hdc, hMenuFont, &menucharsize );
882 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
883 * but it is unlikely an application will depend on that */
884 ODitemheight = HIWORD( GetDialogBaseUnits());
886 mis.CtlType = ODT_MENU;
887 mis.CtlID = 0;
888 mis.itemID = lpitem->wID;
889 mis.itemData = (DWORD)lpitem->dwItemData;
890 mis.itemHeight = ODitemheight;
891 mis.itemWidth = 0;
892 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
893 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
894 * width of a menufont character to the width of an owner-drawn menu.
896 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
897 if (menuBar) {
898 /* under at least win95 you seem to be given a standard
899 height for the menu and the height value is ignored */
900 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
901 } else
902 lpitem->rect.bottom += mis.itemHeight;
904 TRACE("id=%04x size=%ldx%ld\n",
905 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
906 lpitem->rect.bottom-lpitem->rect.top);
907 return;
910 if (lpitem->fType & MF_SEPARATOR)
912 lpitem->rect.bottom += SEPARATOR_HEIGHT;
913 return;
916 if (!menuBar)
918 /* New style MIIM_BITMAP */
919 if (lpitem->hbmpItem)
921 if (lpitem->hbmpItem == HBMMENU_CALLBACK)
923 MEASUREITEMSTRUCT measItem;
924 measItem.CtlType = ODT_MENU;
925 measItem.CtlID = 0;
926 measItem.itemID = lpitem->wID;
927 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
928 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
929 measItem.itemData = lpitem->dwItemData;
931 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
933 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
934 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
935 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
936 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
937 } else {
938 SIZE size;
939 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
940 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
941 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
942 lpitem->rect.right += size.cx;
943 lpitem->rect.bottom += size.cy;
945 if (lppop->dwStyle & MNS_CHECKORBMP)
946 lpitem->rect.right += check_bitmap_width;
947 else
948 lpitem->rect.right += 2 * check_bitmap_width;
949 } else
950 lpitem->rect.right += 2 * check_bitmap_width;
951 if (lpitem->fType & MF_POPUP)
952 lpitem->rect.right += arrow_bitmap_width;
955 if (IS_BITMAP_ITEM(lpitem->fType))
957 SIZE size;
959 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
960 lpitem->rect.right += size.cx;
961 lpitem->rect.bottom += size.cy;
962 /* Leave space for the sunken border */
963 lpitem->rect.right += 2;
964 lpitem->rect.bottom += 2;
968 /* it must be a text item - unless it's the system menu */
969 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
970 { SIZE size;
972 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
974 lpitem->rect.right += size.cx;
975 lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
976 lpitem->xTab = 0;
978 if (menuBar)
980 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
982 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
984 /* Item contains a tab (only meaningful in popup menus) */
985 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
986 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
987 lpitem->rect.right += MENU_TAB_SPACE;
989 else
991 if (strchrW( lpitem->text, '\b' ))
992 lpitem->rect.right += MENU_TAB_SPACE;
993 lpitem->xTab = lpitem->rect.right - check_bitmap_width
994 - arrow_bitmap_width;
997 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
1001 /***********************************************************************
1002 * MENU_PopupMenuCalcSize
1004 * Calculate the size of a popup menu.
1006 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1008 MENUITEM *lpitem;
1009 HDC hdc;
1010 int start, i;
1011 int orgX, orgY, maxX, maxTab, maxTabWidth;
1013 lppop->Width = lppop->Height = 0;
1014 if (lppop->nItems == 0) return;
1015 hdc = GetDC( 0 );
1017 SelectObject( hdc, hMenuFont);
1019 start = 0;
1020 maxX = 2 + 1;
1022 lppop->maxBmpSize.cx = 0;
1023 lppop->maxBmpSize.cy = 0;
1025 while (start < lppop->nItems)
1027 lpitem = &lppop->items[start];
1028 orgX = maxX;
1029 if( lpitem->fType & MF_MENUBREAK)
1030 orgX += MENU_COL_SPACE;
1031 orgY = 3;
1033 maxTab = maxTabWidth = 0;
1034 /* Parse items until column break or end of menu */
1035 for (i = start; i < lppop->nItems; i++, lpitem++)
1037 if ((i != start) &&
1038 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1040 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1042 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1043 maxX = max( maxX, lpitem->rect.right );
1044 orgY = lpitem->rect.bottom;
1045 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1047 maxTab = max( maxTab, lpitem->xTab );
1048 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1052 /* Finish the column (set all items to the largest width found) */
1053 maxX = max( maxX, maxTab + maxTabWidth );
1054 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1056 lpitem->rect.right = maxX;
1057 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1058 lpitem->xTab = maxTab;
1061 lppop->Height = max( lppop->Height, orgY );
1064 lppop->Width = maxX;
1066 /* space for 3d border */
1067 lppop->Height += 2;
1068 lppop->Width += 2;
1070 ReleaseDC( 0, hdc );
1074 /***********************************************************************
1075 * MENU_MenuBarCalcSize
1077 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1078 * height is off by 1 pixel which causes lengthy window relocations when
1079 * active document window is maximized/restored.
1081 * Calculate the size of the menu bar.
1083 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1084 LPPOPUPMENU lppop, HWND hwndOwner )
1086 MENUITEM *lpitem;
1087 int start, i, orgX, orgY, maxY, helpPos;
1089 if ((lprect == NULL) || (lppop == NULL)) return;
1090 if (lppop->nItems == 0) return;
1091 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1092 lprect->left, lprect->top, lprect->right, lprect->bottom);
1093 lppop->Width = lprect->right - lprect->left;
1094 lppop->Height = 0;
1095 maxY = lprect->top+1;
1096 start = 0;
1097 helpPos = -1;
1098 lppop->maxBmpSize.cx = 0;
1099 lppop->maxBmpSize.cy = 0;
1100 while (start < lppop->nItems)
1102 lpitem = &lppop->items[start];
1103 orgX = lprect->left;
1104 orgY = maxY;
1106 /* Parse items until line break or end of menu */
1107 for (i = start; i < lppop->nItems; i++, lpitem++)
1109 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1110 if ((i != start) &&
1111 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1113 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1114 orgX, orgY );
1115 debug_print_menuitem (" item: ", lpitem, "");
1116 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1118 if (lpitem->rect.right > lprect->right)
1120 if (i != start) break;
1121 else lpitem->rect.right = lprect->right;
1123 maxY = max( maxY, lpitem->rect.bottom );
1124 orgX = lpitem->rect.right;
1127 /* Finish the line (set all items to the largest height found) */
1128 while (start < i) lppop->items[start++].rect.bottom = maxY;
1131 lprect->bottom = maxY;
1132 lppop->Height = lprect->bottom - lprect->top;
1134 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1135 /* the last item (if several lines, only move the last line) */
1136 lpitem = &lppop->items[lppop->nItems-1];
1137 orgY = lpitem->rect.top;
1138 orgX = lprect->right;
1139 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1140 if ( (helpPos==-1) || (helpPos>i) )
1141 break; /* done */
1142 if (lpitem->rect.top != orgY) break; /* Other line */
1143 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1144 lpitem->rect.left += orgX - lpitem->rect.right;
1145 lpitem->rect.right = orgX;
1146 orgX = lpitem->rect.left;
1150 /***********************************************************************
1151 * MENU_DrawMenuItem
1153 * Draw a single menu item.
1155 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1156 UINT height, BOOL menuBar, UINT odaction )
1158 RECT rect;
1160 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1162 if (lpitem->fType & MF_SYSMENU)
1164 if( !IsIconic(hwnd) )
1165 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1166 return;
1169 /* Setup colors */
1171 if (lpitem->fState & MF_HILITE)
1173 if(menuBar) {
1174 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1175 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1176 } else {
1177 if(lpitem->fState & MF_GRAYED)
1178 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1179 else
1180 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1181 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1184 else
1186 if (lpitem->fState & MF_GRAYED)
1187 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1188 else
1189 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1190 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1193 if (lpitem->fType & MF_OWNERDRAW)
1196 ** Experimentation under Windows reveals that an owner-drawn
1197 ** menu is given the rectangle which includes the space it requested
1198 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1199 ** and a popup-menu arrow. This is the value of lpitem->rect.
1200 ** Windows will leave all drawing to the application except for
1201 ** the popup-menu arrow. Windows always draws that itself, after
1202 ** the menu owner has finished drawing.
1204 DRAWITEMSTRUCT dis;
1206 dis.CtlType = ODT_MENU;
1207 dis.CtlID = 0;
1208 dis.itemID = lpitem->wID;
1209 dis.itemData = (DWORD)lpitem->dwItemData;
1210 dis.itemState = 0;
1211 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1212 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1213 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1214 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1215 dis.hwndItem = (HWND)hmenu;
1216 dis.hDC = hdc;
1217 dis.rcItem = lpitem->rect;
1218 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1219 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1220 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1221 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1222 dis.rcItem.bottom);
1223 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1224 /* Fall through to draw popup-menu arrow */
1227 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1228 lpitem->rect.right,lpitem->rect.bottom);
1230 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1232 rect = lpitem->rect;
1234 if (!(lpitem->fType & MF_OWNERDRAW))
1236 if (lpitem->fState & MF_HILITE)
1238 if(menuBar)
1239 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1240 else
1241 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1243 else
1244 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1247 SetBkMode( hdc, TRANSPARENT );
1249 if (!(lpitem->fType & MF_OWNERDRAW))
1251 /* vertical separator */
1252 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1254 RECT rc = rect;
1255 rc.top = 3;
1256 rc.bottom = height - 3;
1257 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1260 /* horizontal separator */
1261 if (lpitem->fType & MF_SEPARATOR)
1263 RECT rc = rect;
1264 rc.left++;
1265 rc.right--;
1266 rc.top += SEPARATOR_HEIGHT / 2;
1267 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1268 return;
1272 /* helper lines for debugging */
1273 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1274 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1275 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1276 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1279 if (!menuBar)
1281 HBITMAP bm;
1282 INT y = rect.top + rect.bottom;
1283 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1284 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1286 if (!(lpitem->fType & MF_OWNERDRAW))
1288 /* New style MIIM_BITMAP */
1289 if (lpitem->hbmpItem)
1291 POPUPMENU *menu = MENU_GetMenu(hmenu);
1292 HBITMAP hbm = lpitem->hbmpItem;
1294 if (hbm == HBMMENU_CALLBACK)
1296 DRAWITEMSTRUCT drawItem;
1297 drawItem.CtlType = ODT_MENU;
1298 drawItem.CtlID = 0;
1299 drawItem.itemID = lpitem->wID;
1300 drawItem.itemAction = odaction;
1301 drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1302 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1303 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1304 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1305 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1306 drawItem.hwndItem = (HWND)hmenu;
1307 drawItem.hDC = hdc;
1308 drawItem.rcItem = lpitem->rect;
1309 drawItem.itemData = lpitem->dwItemData;
1311 if (!(lpitem->fState & MF_CHECKED))
1312 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1314 } else {
1315 MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1317 if (menu->dwStyle & MNS_CHECKORBMP)
1318 rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1319 else
1320 rect.left += menu->maxBmpSize.cx;
1322 /* Draw the check mark
1324 * FIXME:
1325 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1327 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1328 if (bm) /* we have a custom bitmap */
1330 HDC hdcMem = CreateCompatibleDC( hdc );
1331 SelectObject( hdcMem, bm );
1332 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1333 check_bitmap_width, check_bitmap_height,
1334 hdcMem, 0, 0, SRCCOPY );
1335 DeleteDC( hdcMem );
1337 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1339 RECT r;
1340 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1341 HDC hdcMem = CreateCompatibleDC( hdc );
1342 SelectObject( hdcMem, bm );
1343 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1344 DrawFrameControl( hdcMem, &r, DFC_MENU,
1345 (lpitem->fType & MFT_RADIOCHECK) ?
1346 DFCS_MENUBULLET : DFCS_MENUCHECK );
1347 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1348 hdcMem, 0, 0, SRCCOPY );
1349 DeleteDC( hdcMem );
1350 DeleteObject( bm );
1354 /* Draw the popup-menu arrow */
1355 if (lpitem->fType & MF_POPUP)
1357 HDC hdcMem = CreateCompatibleDC( hdc );
1358 HBITMAP hOrigBitmap;
1360 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1361 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1362 (y - arrow_bitmap_height) / 2,
1363 arrow_bitmap_width, arrow_bitmap_height,
1364 hdcMem, 0, 0, SRCCOPY );
1365 SelectObject( hdcMem, hOrigBitmap );
1366 DeleteDC( hdcMem );
1369 rect.left += check_bitmap_width;
1370 rect.right -= arrow_bitmap_width;
1373 /* Done for owner-drawn */
1374 if (lpitem->fType & MF_OWNERDRAW)
1375 return;
1377 /* Draw the item text or bitmap */
1378 if (IS_BITMAP_ITEM(lpitem->fType))
1380 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1381 return;
1383 /* No bitmap - process text if present */
1384 else if (IS_STRING_ITEM(lpitem->fType))
1386 register int i;
1387 HFONT hfontOld = 0;
1389 UINT uFormat = (menuBar) ?
1390 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1391 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1393 if ( lpitem->fState & MFS_DEFAULT )
1395 hfontOld = SelectObject( hdc, hMenuFontBold);
1398 if (menuBar)
1400 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1401 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1404 for (i = 0; lpitem->text[i]; i++)
1405 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1406 break;
1408 if(lpitem->fState & MF_GRAYED)
1410 if (!(lpitem->fState & MF_HILITE) )
1412 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1413 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1414 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1415 --rect.left; --rect.top; --rect.right; --rect.bottom;
1417 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1420 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1422 /* paint the shortcut text */
1423 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1425 if (lpitem->text[i] == '\t')
1427 rect.left = lpitem->xTab;
1428 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1430 else
1432 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1435 if(lpitem->fState & MF_GRAYED)
1437 if (!(lpitem->fState & MF_HILITE) )
1439 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1440 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1441 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1442 --rect.left; --rect.top; --rect.right; --rect.bottom;
1444 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1446 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1449 if (hfontOld)
1450 SelectObject (hdc, hfontOld);
1455 /***********************************************************************
1456 * MENU_DrawPopupMenu
1458 * Paint a popup menu.
1460 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1462 HBRUSH hPrevBrush = 0;
1463 RECT rect;
1465 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1467 GetClientRect( hwnd, &rect );
1469 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1470 && (SelectObject( hdc, hMenuFont)))
1472 HPEN hPrevPen;
1474 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1476 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1477 if( hPrevPen )
1479 POPUPMENU *menu;
1481 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1483 /* draw menu items */
1485 menu = MENU_GetMenu( hmenu );
1486 if (menu && menu->nItems)
1488 MENUITEM *item;
1489 UINT u;
1491 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1492 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1493 menu->Height, FALSE, ODA_DRAWENTIRE );
1496 } else
1498 SelectObject( hdc, hPrevBrush );
1503 /***********************************************************************
1504 * MENU_DrawMenuBar
1506 * Paint a menu bar. Returns the height of the menu bar.
1507 * called from [windows/nonclient.c]
1509 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1510 BOOL suppress_draw)
1512 LPPOPUPMENU lppop;
1513 HFONT hfontOld = 0;
1514 HMENU hMenu = GetMenu(hwnd);
1516 lppop = MENU_GetMenu( hMenu );
1517 if (lppop == NULL || lprect == NULL)
1519 return GetSystemMetrics(SM_CYMENU);
1522 if (suppress_draw)
1524 hfontOld = SelectObject( hDC, hMenuFont);
1526 if (lppop->Height == 0)
1527 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1529 lprect->bottom = lprect->top + lppop->Height;
1531 if (hfontOld) SelectObject( hDC, hfontOld);
1532 return lppop->Height;
1534 else
1535 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1539 /***********************************************************************
1540 * MENU_ShowPopup
1542 * Display a popup menu.
1544 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1545 INT x, INT y, INT xanchor, INT yanchor )
1547 POPUPMENU *menu;
1548 UINT width, height;
1550 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1551 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1553 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1554 if (menu->FocusedItem != NO_SELECTED_ITEM)
1556 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1557 menu->FocusedItem = NO_SELECTED_ITEM;
1560 /* store the owner for DrawItem */
1561 menu->hwndOwner = hwndOwner;
1563 MENU_PopupMenuCalcSize( menu, hwndOwner );
1565 /* adjust popup menu pos so that it fits within the desktop */
1567 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1568 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1570 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1572 if( xanchor )
1573 x -= width - xanchor;
1574 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1575 x = GetSystemMetrics(SM_CXSCREEN) - width;
1577 if( x < 0 ) x = 0;
1579 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1581 if( yanchor )
1582 y -= height + yanchor;
1583 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1584 y = GetSystemMetrics(SM_CYSCREEN) - height;
1586 if( y < 0 ) y = 0;
1588 /* NOTE: In Windows, top menu popup is not owned. */
1589 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1590 WS_POPUP, x, y, width, height,
1591 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1592 (LPVOID)hmenu );
1593 if( !menu->hWnd ) return FALSE;
1594 if (!top_popup) top_popup = menu->hWnd;
1596 /* Display the window */
1598 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1599 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1600 UpdateWindow( menu->hWnd );
1601 return TRUE;
1605 /***********************************************************************
1606 * MENU_SelectItem
1608 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1609 BOOL sendMenuSelect, HMENU topmenu )
1611 LPPOPUPMENU lppop;
1612 HDC hdc;
1614 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1616 lppop = MENU_GetMenu( hmenu );
1617 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1619 if (lppop->FocusedItem == wIndex) return;
1620 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1621 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1622 if (!top_popup) top_popup = lppop->hWnd;
1624 SelectObject( hdc, hMenuFont);
1626 /* Clear previous highlighted item */
1627 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1629 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1630 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1631 lppop->Height, !(lppop->wFlags & MF_POPUP),
1632 ODA_SELECT );
1635 /* Highlight new item (if any) */
1636 lppop->FocusedItem = wIndex;
1637 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1639 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1640 lppop->items[wIndex].fState |= MF_HILITE;
1641 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1642 &lppop->items[wIndex], lppop->Height,
1643 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1645 if (sendMenuSelect)
1647 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1648 SendMessageW( hwndOwner, WM_MENUSELECT,
1649 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1650 ip->fType | ip->fState |
1651 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1654 else if (sendMenuSelect) {
1655 if(topmenu){
1656 int pos;
1657 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1658 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1659 MENUITEM *ip = &ptm->items[pos];
1660 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1661 ip->fType | ip->fState |
1662 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1666 ReleaseDC( lppop->hWnd, hdc );
1670 /***********************************************************************
1671 * MENU_MoveSelection
1673 * Moves currently selected item according to the offset parameter.
1674 * If there is no selection then it should select the last item if
1675 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1677 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1679 INT i;
1680 POPUPMENU *menu;
1682 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1684 menu = MENU_GetMenu( hmenu );
1685 if ((!menu) || (!menu->items)) return;
1687 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1689 if( menu->nItems == 1 ) return; else
1690 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1691 ; i += offset)
1692 if (!(menu->items[i].fType & MF_SEPARATOR))
1694 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1695 return;
1699 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1700 i >= 0 && i < menu->nItems ; i += offset)
1701 if (!(menu->items[i].fType & MF_SEPARATOR))
1703 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1704 return;
1709 /**********************************************************************
1710 * MENU_SetItemData
1712 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1713 * ModifyMenu().
1715 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1716 LPCWSTR str )
1718 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1720 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1721 TRACE("flags=%x str=%p\n", flags, str);
1723 if (IS_STRING_ITEM(flags))
1725 if (!str)
1727 flags |= MF_SEPARATOR;
1728 item->text = NULL;
1730 else
1732 LPWSTR text;
1733 /* Item beginning with a backspace is a help item */
1734 if (*str == '\b')
1736 flags |= MF_HELP;
1737 str++;
1739 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1740 return FALSE;
1741 strcpyW( text, str );
1742 item->text = text;
1745 else if (IS_BITMAP_ITEM(flags))
1746 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1747 else item->text = NULL;
1749 if (flags & MF_OWNERDRAW)
1750 item->dwItemData = (DWORD)str;
1751 else
1752 item->dwItemData = 0;
1754 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1755 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1757 if (flags & MF_POPUP)
1759 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1760 if (menu) menu->wFlags |= MF_POPUP;
1761 else
1763 item->wID = 0;
1764 item->hSubMenu = 0;
1765 item->fType = 0;
1766 item->fState = 0;
1767 return FALSE;
1771 item->wID = id;
1772 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1774 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1775 flags |= MF_POPUP; /* keep popup */
1777 item->fType = flags & TYPE_MASK;
1778 item->fState = (flags & STATE_MASK) &
1779 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1782 /* Don't call SetRectEmpty here! */
1785 HeapFree( GetProcessHeap(), 0, prevText );
1787 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1788 return TRUE;
1792 /**********************************************************************
1793 * MENU_InsertItem
1795 * Insert (allocate) a new item into a menu.
1797 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1799 MENUITEM *newItems;
1800 POPUPMENU *menu;
1802 if (!(menu = MENU_GetMenu(hMenu)))
1803 return NULL;
1805 /* Find where to insert new item */
1807 if (flags & MF_BYPOSITION) {
1808 if (pos > menu->nItems)
1809 pos = menu->nItems;
1810 } else {
1811 if (!MENU_FindItem( &hMenu, &pos, flags ))
1812 pos = menu->nItems;
1813 else {
1814 if (!(menu = MENU_GetMenu( hMenu )))
1815 return NULL;
1819 /* Create new items array */
1821 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1822 if (!newItems)
1824 WARN("allocation failed\n" );
1825 return NULL;
1827 if (menu->nItems > 0)
1829 /* Copy the old array into the new one */
1830 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1831 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1832 (menu->nItems-pos)*sizeof(MENUITEM) );
1833 HeapFree( GetProcessHeap(), 0, menu->items );
1835 menu->items = newItems;
1836 menu->nItems++;
1837 memset( &newItems[pos], 0, sizeof(*newItems) );
1838 menu->Height = 0; /* force size recalculate */
1839 return &newItems[pos];
1843 /**********************************************************************
1844 * MENU_ParseResource
1846 * Parse a standard menu resource and add items to the menu.
1847 * Return a pointer to the end of the resource.
1849 * NOTE: flags is equivalent to the mtOption field
1851 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1853 WORD flags, id = 0;
1854 LPCSTR str;
1858 flags = GET_WORD(res);
1859 res += sizeof(WORD);
1860 if (!(flags & MF_POPUP))
1862 id = GET_WORD(res);
1863 res += sizeof(WORD);
1865 str = res;
1866 if (!unicode) res += strlen(str) + 1;
1867 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1868 if (flags & MF_POPUP)
1870 HMENU hSubMenu = CreatePopupMenu();
1871 if (!hSubMenu) return NULL;
1872 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1873 return NULL;
1874 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1875 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1877 else /* Not a popup */
1879 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1880 else AppendMenuW( hMenu, flags, id,
1881 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1883 } while (!(flags & MF_END));
1884 return res;
1888 /**********************************************************************
1889 * MENUEX_ParseResource
1891 * Parse an extended menu resource and add items to the menu.
1892 * Return a pointer to the end of the resource.
1894 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1896 WORD resinfo;
1897 do {
1898 MENUITEMINFOW mii;
1900 mii.cbSize = sizeof(mii);
1901 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1902 mii.fType = GET_DWORD(res);
1903 res += sizeof(DWORD);
1904 mii.fState = GET_DWORD(res);
1905 res += sizeof(DWORD);
1906 mii.wID = GET_DWORD(res);
1907 res += sizeof(DWORD);
1908 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1909 res += sizeof(WORD);
1910 /* Align the text on a word boundary. */
1911 res += (~((int)res - 1)) & 1;
1912 mii.dwTypeData = (LPWSTR) res;
1913 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1914 /* Align the following fields on a dword boundary. */
1915 res += (~((int)res - 1)) & 3;
1917 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1918 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1920 if (resinfo & 1) { /* Pop-up? */
1921 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1922 res += sizeof(DWORD);
1923 mii.hSubMenu = CreatePopupMenu();
1924 if (!mii.hSubMenu)
1925 return NULL;
1926 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1927 DestroyMenu(mii.hSubMenu);
1928 return NULL;
1930 mii.fMask |= MIIM_SUBMENU;
1931 mii.fType |= MF_POPUP;
1933 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1935 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1936 mii.wID, mii.fType);
1937 mii.fType |= MF_SEPARATOR;
1939 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1940 } while (!(resinfo & MF_END));
1941 return res;
1945 /***********************************************************************
1946 * MENU_GetSubPopup
1948 * Return the handle of the selected sub-popup menu (if any).
1950 static HMENU MENU_GetSubPopup( HMENU hmenu )
1952 POPUPMENU *menu;
1953 MENUITEM *item;
1955 menu = MENU_GetMenu( hmenu );
1957 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1959 item = &menu->items[menu->FocusedItem];
1960 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1961 return item->hSubMenu;
1962 return 0;
1966 /***********************************************************************
1967 * MENU_HideSubPopups
1969 * Hide the sub-popup menus of this menu.
1971 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1972 BOOL sendMenuSelect )
1974 POPUPMENU *menu = MENU_GetMenu( hmenu );
1976 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1978 if (menu && top_popup)
1980 HMENU hsubmenu;
1981 POPUPMENU *submenu;
1982 MENUITEM *item;
1984 if (menu->FocusedItem != NO_SELECTED_ITEM)
1986 item = &menu->items[menu->FocusedItem];
1987 if (!(item->fType & MF_POPUP) ||
1988 !(item->fState & MF_MOUSESELECT)) return;
1989 item->fState &= ~MF_MOUSESELECT;
1990 hsubmenu = item->hSubMenu;
1991 } else return;
1993 submenu = MENU_GetMenu( hsubmenu );
1994 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1995 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1996 DestroyWindow( submenu->hWnd );
1997 submenu->hWnd = 0;
2002 /***********************************************************************
2003 * MENU_ShowSubPopup
2005 * Display the sub-menu of the selected item of this menu.
2006 * Return the handle of the submenu, or hmenu if no submenu to display.
2008 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2009 BOOL selectFirst, UINT wFlags )
2011 RECT rect;
2012 POPUPMENU *menu;
2013 MENUITEM *item;
2014 HDC hdc;
2016 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2018 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2020 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2022 item = &menu->items[menu->FocusedItem];
2023 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2024 return hmenu;
2026 /* message must be sent before using item,
2027 because nearly everything may be changed by the application ! */
2029 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2030 if (!(wFlags & TPM_NONOTIFY))
2031 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2032 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2034 item = &menu->items[menu->FocusedItem];
2035 rect = item->rect;
2037 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2038 if (!(item->fState & MF_HILITE))
2040 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2041 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2043 SelectObject( hdc, hMenuFont);
2045 item->fState |= MF_HILITE;
2046 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2047 ReleaseDC( menu->hWnd, hdc );
2049 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2050 item->rect = rect;
2052 item->fState |= MF_MOUSESELECT;
2054 if (IS_SYSTEM_MENU(menu))
2056 MENU_InitSysMenuPopup(item->hSubMenu,
2057 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2058 GetClassLongW( menu->hWnd, GCL_STYLE));
2060 NC_GetSysPopupPos( menu->hWnd, &rect );
2061 rect.top = rect.bottom;
2062 rect.right = GetSystemMetrics(SM_CXSIZE);
2063 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2065 else
2067 GetWindowRect( menu->hWnd, &rect );
2068 if (menu->wFlags & MF_POPUP)
2070 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2071 rect.top += item->rect.top;
2072 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2073 rect.bottom = item->rect.top - item->rect.bottom;
2075 else
2077 rect.left += item->rect.left;
2078 rect.top += item->rect.bottom;
2079 rect.right = item->rect.right - item->rect.left;
2080 rect.bottom = item->rect.bottom - item->rect.top;
2084 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2085 rect.left, rect.top, rect.right, rect.bottom );
2086 if (selectFirst)
2087 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2088 return item->hSubMenu;
2093 /**********************************************************************
2094 * MENU_IsMenuActive
2096 HWND MENU_IsMenuActive(void)
2098 return top_popup;
2101 /***********************************************************************
2102 * MENU_PtMenu
2104 * Walks menu chain trying to find a menu pt maps to.
2106 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2108 POPUPMENU *menu = MENU_GetMenu( hMenu );
2109 UINT item = menu->FocusedItem;
2110 HMENU ret;
2112 /* try subpopup first (if any) */
2113 ret = (item != NO_SELECTED_ITEM &&
2114 (menu->items[item].fType & MF_POPUP) &&
2115 (menu->items[item].fState & MF_MOUSESELECT))
2116 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2118 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2120 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2121 if( menu->wFlags & MF_POPUP )
2123 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2125 else if (ht == HTSYSMENU)
2126 ret = get_win_sys_menu( menu->hWnd );
2127 else if (ht == HTMENU)
2128 ret = GetMenu( menu->hWnd );
2130 return ret;
2133 /***********************************************************************
2134 * MENU_ExecFocusedItem
2136 * Execute a menu item (for instance when user pressed Enter).
2137 * Return the wID of the executed item. Otherwise, -1 indicating
2138 * that no menu item was executed;
2139 * Have to receive the flags for the TrackPopupMenu options to avoid
2140 * sending unwanted message.
2143 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2145 MENUITEM *item;
2146 POPUPMENU *menu = MENU_GetMenu( hMenu );
2148 TRACE("%p hmenu=%p\n", pmt, hMenu);
2150 if (!menu || !menu->nItems ||
2151 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2153 item = &menu->items[menu->FocusedItem];
2155 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2157 if (!(item->fType & MF_POPUP))
2159 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2161 /* If TPM_RETURNCMD is set you return the id, but
2162 do not send a message to the owner */
2163 if(!(wFlags & TPM_RETURNCMD))
2165 if( menu->wFlags & MF_SYSMENU )
2166 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2167 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2168 else
2169 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2171 return item->wID;
2174 else
2175 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2177 return -1;
2180 /***********************************************************************
2181 * MENU_SwitchTracking
2183 * Helper function for menu navigation routines.
2185 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2187 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2188 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2190 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2192 if( pmt->hTopMenu != hPtMenu &&
2193 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2195 /* both are top level menus (system and menu-bar) */
2196 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2197 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2198 pmt->hTopMenu = hPtMenu;
2200 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2201 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2205 /***********************************************************************
2206 * MENU_ButtonDown
2208 * Return TRUE if we can go on with menu tracking.
2210 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2212 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2214 if (hPtMenu)
2216 UINT id = 0;
2217 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2218 MENUITEM *item;
2220 if( IS_SYSTEM_MENU(ptmenu) )
2221 item = ptmenu->items;
2222 else
2223 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2225 if( item )
2227 if( ptmenu->FocusedItem != id )
2228 MENU_SwitchTracking( pmt, hPtMenu, id );
2230 /* If the popup menu is not already "popped" */
2231 if(!(item->fState & MF_MOUSESELECT ))
2233 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2236 return TRUE;
2238 /* Else the click was on the menu bar, finish the tracking */
2240 return FALSE;
2243 /***********************************************************************
2244 * MENU_ButtonUp
2246 * Return the value of MENU_ExecFocusedItem if
2247 * the selected item was not a popup. Else open the popup.
2248 * A -1 return value indicates that we go on with menu tracking.
2251 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2253 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2255 if (hPtMenu)
2257 UINT id = 0;
2258 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2259 MENUITEM *item;
2261 if( IS_SYSTEM_MENU(ptmenu) )
2262 item = ptmenu->items;
2263 else
2264 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2266 if( item && (ptmenu->FocusedItem == id ))
2268 if( !(item->fType & MF_POPUP) )
2269 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2271 /* If we are dealing with the top-level menu */
2272 /* and this is a click on an already "popped" item: */
2273 /* Stop the menu tracking and close the opened submenus */
2274 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2275 return 0;
2277 ptmenu->bTimeToHide = TRUE;
2279 return -1;
2283 /***********************************************************************
2284 * MENU_MouseMove
2286 * Return TRUE if we can go on with menu tracking.
2288 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2290 UINT id = NO_SELECTED_ITEM;
2291 POPUPMENU *ptmenu = NULL;
2293 if( hPtMenu )
2295 ptmenu = MENU_GetMenu( hPtMenu );
2296 if( IS_SYSTEM_MENU(ptmenu) )
2297 id = 0;
2298 else
2299 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2302 if( id == NO_SELECTED_ITEM )
2304 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2305 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2308 else if( ptmenu->FocusedItem != id )
2310 MENU_SwitchTracking( pmt, hPtMenu, id );
2311 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2313 return TRUE;
2317 /***********************************************************************
2318 * MENU_SetCapture
2320 static void MENU_SetCapture( HWND hwnd )
2322 HWND previous = 0;
2324 SERVER_START_REQ( set_capture_window )
2326 req->handle = hwnd;
2327 req->flags = CAPTURE_MENU;
2328 if (!wine_server_call_err( req ))
2330 previous = reply->previous;
2331 hwnd = reply->full_handle;
2334 SERVER_END_REQ;
2336 if (previous && previous != hwnd)
2337 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2341 /***********************************************************************
2342 * MENU_DoNextMenu
2344 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2346 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2348 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2350 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2351 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2353 MDINEXTMENU next_menu;
2354 HMENU hNewMenu;
2355 HWND hNewWnd;
2356 UINT id = 0;
2358 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2359 next_menu.hmenuNext = 0;
2360 next_menu.hwndNext = 0;
2361 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2363 TRACE("%p [%p] -> %p [%p]\n",
2364 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2366 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2368 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2369 hNewWnd = pmt->hOwnerWnd;
2370 if( IS_SYSTEM_MENU(menu) )
2372 /* switch to the menu bar */
2374 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2376 if( vk == VK_LEFT )
2378 menu = MENU_GetMenu( hNewMenu );
2379 id = menu->nItems - 1;
2382 else if (style & WS_SYSMENU )
2384 /* switch to the system menu */
2385 hNewMenu = get_win_sys_menu( hNewWnd );
2387 else return FALSE;
2389 else /* application returned a new menu to switch to */
2391 hNewMenu = next_menu.hmenuNext;
2392 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2394 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2396 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2398 if (style & WS_SYSMENU &&
2399 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2401 /* get the real system menu */
2402 hNewMenu = get_win_sys_menu(hNewWnd);
2404 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2406 /* FIXME: Not sure what to do here;
2407 * perhaps try to track hNewMenu as a popup? */
2409 TRACE(" -- got confused.\n");
2410 return FALSE;
2413 else return FALSE;
2416 if( hNewMenu != pmt->hTopMenu )
2418 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2419 FALSE, 0 );
2420 if( pmt->hCurrentMenu != pmt->hTopMenu )
2421 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2424 if( hNewWnd != pmt->hOwnerWnd )
2426 pmt->hOwnerWnd = hNewWnd;
2427 MENU_SetCapture( pmt->hOwnerWnd );
2430 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2431 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2433 return TRUE;
2435 return FALSE;
2438 /***********************************************************************
2439 * MENU_SuspendPopup
2441 * The idea is not to show the popup if the next input message is
2442 * going to hide it anyway.
2444 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2446 MSG msg;
2448 msg.hwnd = pmt->hOwnerWnd;
2450 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2451 pmt->trackFlags |= TF_SKIPREMOVE;
2453 switch( uMsg )
2455 case WM_KEYDOWN:
2456 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2457 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2459 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2460 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2461 if( msg.message == WM_KEYDOWN &&
2462 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2464 pmt->trackFlags |= TF_SUSPENDPOPUP;
2465 return TRUE;
2468 break;
2471 /* failures go through this */
2472 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2473 return FALSE;
2476 /***********************************************************************
2477 * MENU_KeyEscape
2479 * Handle a VK_ESCAPE key event in a menu.
2481 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2483 BOOL bEndMenu = TRUE;
2485 if (pmt->hCurrentMenu != pmt->hTopMenu)
2487 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2489 if (menu->wFlags & MF_POPUP)
2491 HMENU hmenutmp, hmenuprev;
2493 hmenuprev = hmenutmp = pmt->hTopMenu;
2495 /* close topmost popup */
2496 while (hmenutmp != pmt->hCurrentMenu)
2498 hmenuprev = hmenutmp;
2499 hmenutmp = MENU_GetSubPopup( hmenuprev );
2502 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2503 pmt->hCurrentMenu = hmenuprev;
2504 bEndMenu = FALSE;
2508 return bEndMenu;
2511 /***********************************************************************
2512 * MENU_KeyLeft
2514 * Handle a VK_LEFT key event in a menu.
2516 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2518 POPUPMENU *menu;
2519 HMENU hmenutmp, hmenuprev;
2520 UINT prevcol;
2522 hmenuprev = hmenutmp = pmt->hTopMenu;
2523 menu = MENU_GetMenu( hmenutmp );
2525 /* Try to move 1 column left (if possible) */
2526 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2527 NO_SELECTED_ITEM ) {
2529 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2530 prevcol, TRUE, 0 );
2531 return;
2534 /* close topmost popup */
2535 while (hmenutmp != pmt->hCurrentMenu)
2537 hmenuprev = hmenutmp;
2538 hmenutmp = MENU_GetSubPopup( hmenuprev );
2541 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2542 pmt->hCurrentMenu = hmenuprev;
2544 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2546 /* move menu bar selection if no more popups are left */
2548 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2549 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2551 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2553 /* A sublevel menu was displayed - display the next one
2554 * unless there is another displacement coming up */
2556 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2557 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2558 pmt->hTopMenu, TRUE, wFlags);
2564 /***********************************************************************
2565 * MENU_KeyRight
2567 * Handle a VK_RIGHT key event in a menu.
2569 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2571 HMENU hmenutmp;
2572 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2573 UINT nextcol;
2575 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2576 pmt->hCurrentMenu,
2577 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2578 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2580 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2582 /* If already displaying a popup, try to display sub-popup */
2584 hmenutmp = pmt->hCurrentMenu;
2585 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2587 /* if subpopup was displayed then we are done */
2588 if (hmenutmp != pmt->hCurrentMenu) return;
2591 /* Check to see if there's another column */
2592 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2593 NO_SELECTED_ITEM ) {
2594 TRACE("Going to %d.\n", nextcol );
2595 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2596 nextcol, TRUE, 0 );
2597 return;
2600 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2602 if( pmt->hCurrentMenu != pmt->hTopMenu )
2604 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2605 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2606 } else hmenutmp = 0;
2608 /* try to move to the next item */
2609 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2610 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2612 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2613 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2614 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2615 pmt->hTopMenu, TRUE, wFlags);
2619 /***********************************************************************
2620 * MENU_TrackMenu
2622 * Menu tracking code.
2624 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2625 HWND hwnd, const RECT *lprect )
2627 MSG msg;
2628 POPUPMENU *menu;
2629 BOOL fRemove;
2630 INT executedMenuId = -1;
2631 MTRACKER mt;
2632 BOOL enterIdleSent = FALSE;
2634 mt.trackFlags = 0;
2635 mt.hCurrentMenu = hmenu;
2636 mt.hTopMenu = hmenu;
2637 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2638 mt.pt.x = x;
2639 mt.pt.y = y;
2641 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2642 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2643 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2645 fEndMenu = FALSE;
2646 if (!(menu = MENU_GetMenu( hmenu )))
2648 WARN("Invalid menu handle %p\n", hmenu);
2649 SetLastError(ERROR_INVALID_MENU_HANDLE);
2650 return FALSE;
2653 if (wFlags & TPM_BUTTONDOWN)
2655 /* Get the result in order to start the tracking or not */
2656 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2657 fEndMenu = !fRemove;
2660 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2662 MENU_SetCapture( mt.hOwnerWnd );
2664 while (!fEndMenu)
2666 menu = MENU_GetMenu( mt.hCurrentMenu );
2667 if (!menu) /* sometimes happens if I do a window manager close */
2668 break;
2670 /* we have to keep the message in the queue until it's
2671 * clear that menu loop is not over yet. */
2673 for (;;)
2675 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2677 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2678 /* remove the message from the queue */
2679 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2681 else
2683 if (!enterIdleSent)
2685 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2686 enterIdleSent = TRUE;
2687 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2689 WaitMessage();
2693 /* check if EndMenu() tried to cancel us, by posting this message */
2694 if(msg.message == WM_CANCELMODE)
2696 /* we are now out of the loop */
2697 fEndMenu = TRUE;
2699 /* remove the message from the queue */
2700 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2702 /* break out of internal loop, ala ESCAPE */
2703 break;
2706 TranslateMessage( &msg );
2707 mt.pt = msg.pt;
2709 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2710 enterIdleSent=FALSE;
2712 fRemove = FALSE;
2713 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2716 * Use the mouse coordinates in lParam instead of those in the MSG
2717 * struct to properly handle synthetic messages. They are already
2718 * in screen coordinates.
2720 mt.pt.x = (short)LOWORD(msg.lParam);
2721 mt.pt.y = (short)HIWORD(msg.lParam);
2723 /* Find a menu for this mouse event */
2724 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2726 switch(msg.message)
2728 /* no WM_NC... messages in captured state */
2730 case WM_RBUTTONDBLCLK:
2731 case WM_RBUTTONDOWN:
2732 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2733 /* fall through */
2734 case WM_LBUTTONDBLCLK:
2735 case WM_LBUTTONDOWN:
2736 /* If the message belongs to the menu, removes it from the queue */
2737 /* Else, end menu tracking */
2738 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2739 fEndMenu = !fRemove;
2740 break;
2742 case WM_RBUTTONUP:
2743 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2744 /* fall through */
2745 case WM_LBUTTONUP:
2746 /* Check if a menu was selected by the mouse */
2747 if (hmenu)
2749 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2751 /* End the loop if executedMenuId is an item ID */
2752 /* or if the job was done (executedMenuId = 0). */
2753 fEndMenu = fRemove = (executedMenuId != -1);
2755 /* No menu was selected by the mouse */
2756 /* if the function was called by TrackPopupMenu, continue
2757 with the menu tracking. If not, stop it */
2758 else
2759 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2761 break;
2763 case WM_MOUSEMOVE:
2764 /* the selected menu item must be changed every time */
2765 /* the mouse moves. */
2767 if (hmenu)
2768 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2770 } /* switch(msg.message) - mouse */
2772 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2774 fRemove = TRUE; /* Keyboard messages are always removed */
2775 switch(msg.message)
2777 case WM_KEYDOWN:
2778 case WM_SYSKEYDOWN:
2779 switch(msg.wParam)
2781 case VK_MENU:
2782 fEndMenu = TRUE;
2783 break;
2785 case VK_HOME:
2786 case VK_END:
2787 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2788 NO_SELECTED_ITEM, FALSE, 0 );
2789 /* fall through */
2790 case VK_UP:
2791 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2792 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2793 break;
2795 case VK_DOWN: /* If on menu bar, pull-down the menu */
2797 menu = MENU_GetMenu( mt.hCurrentMenu );
2798 if (!(menu->wFlags & MF_POPUP))
2799 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2800 else /* otherwise try to move selection */
2801 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2802 break;
2804 case VK_LEFT:
2805 MENU_KeyLeft( &mt, wFlags );
2806 break;
2808 case VK_RIGHT:
2809 MENU_KeyRight( &mt, wFlags );
2810 break;
2812 case VK_ESCAPE:
2813 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2814 break;
2816 case VK_F1:
2818 HELPINFO hi;
2819 hi.cbSize = sizeof(HELPINFO);
2820 hi.iContextType = HELPINFO_MENUITEM;
2821 if (menu->FocusedItem == NO_SELECTED_ITEM)
2822 hi.iCtrlId = 0;
2823 else
2824 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2825 hi.hItemHandle = hmenu;
2826 hi.dwContextId = menu->dwContextHelpID;
2827 hi.MousePos = msg.pt;
2828 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2829 break;
2832 default:
2833 break;
2835 break; /* WM_KEYDOWN */
2837 case WM_CHAR:
2838 case WM_SYSCHAR:
2840 UINT pos;
2842 if (msg.wParam == '\r' || msg.wParam == ' ')
2844 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2845 fEndMenu = (executedMenuId != -1);
2847 break;
2850 /* Hack to avoid control chars. */
2851 /* We will find a better way real soon... */
2852 if (msg.wParam < 32) break;
2854 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2855 LOWORD(msg.wParam), FALSE );
2856 if (pos == (UINT)-2) fEndMenu = TRUE;
2857 else if (pos == (UINT)-1) MessageBeep(0);
2858 else
2860 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2861 TRUE, 0 );
2862 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2863 fEndMenu = (executedMenuId != -1);
2866 break;
2867 } /* switch(msg.message) - kbd */
2869 else
2871 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2872 DispatchMessageW( &msg );
2873 continue;
2876 if (!fEndMenu) fRemove = TRUE;
2878 /* finally remove message from the queue */
2880 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2881 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2882 else mt.trackFlags &= ~TF_SKIPREMOVE;
2885 MENU_SetCapture(0); /* release the capture */
2887 /* If dropdown is still painted and the close box is clicked on
2888 then the menu will be destroyed as part of the DispatchMessage above.
2889 This will then invalidate the menu handle in mt.hTopMenu. We should
2890 check for this first. */
2891 if( IsMenu( mt.hTopMenu ) )
2893 menu = MENU_GetMenu( mt.hTopMenu );
2895 if( IsWindow( mt.hOwnerWnd ) )
2897 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2899 if (menu && (menu->wFlags & MF_POPUP))
2901 DestroyWindow( menu->hWnd );
2902 menu->hWnd = 0;
2904 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2905 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2908 /* Reset the variable for hiding menu */
2909 if( menu ) menu->bTimeToHide = FALSE;
2912 /* The return value is only used by TrackPopupMenu */
2913 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2914 if (executedMenuId == -1) executedMenuId = 0;
2915 return executedMenuId;
2918 /***********************************************************************
2919 * MENU_InitTracking
2921 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2923 POPUPMENU *menu;
2925 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2927 HideCaret(0);
2929 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2930 if (!(wFlags & TPM_NONOTIFY))
2931 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2933 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2935 if (!(wFlags & TPM_NONOTIFY))
2937 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2938 /* If an app changed/recreated menu bar entries in WM_INITMENU
2939 * menu sizes will be recalculated once the menu created/shown.
2943 /* This makes the menus of applications built with Delphi work.
2944 * It also enables menus to be displayed in more than one window,
2945 * but there are some bugs left that need to be fixed in this case.
2947 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2949 return TRUE;
2951 /***********************************************************************
2952 * MENU_ExitTracking
2954 static BOOL MENU_ExitTracking(HWND hWnd)
2956 TRACE("hwnd=%p\n", hWnd);
2958 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2959 ShowCaret(0);
2960 top_popup = 0;
2961 return TRUE;
2964 /***********************************************************************
2965 * MENU_TrackMouseMenuBar
2967 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2969 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2971 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2972 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2974 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2976 if (IsMenu(hMenu))
2978 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2979 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2980 MENU_ExitTracking(hWnd);
2985 /***********************************************************************
2986 * MENU_TrackKbdMenuBar
2988 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2990 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2992 UINT uItem = NO_SELECTED_ITEM;
2993 HMENU hTrackMenu;
2994 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2996 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2998 /* find window that has a menu */
3000 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3001 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3003 /* check if we have to track a system menu */
3005 hTrackMenu = GetMenu( hwnd );
3006 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3008 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3009 hTrackMenu = get_win_sys_menu( hwnd );
3010 uItem = 0;
3011 wParam |= HTSYSMENU; /* prevent item lookup */
3014 if (!IsMenu( hTrackMenu )) return;
3016 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3018 if( wChar && wChar != ' ' )
3020 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3021 if ( uItem >= (UINT)(-2) )
3023 if( uItem == (UINT)(-1) ) MessageBeep(0);
3024 /* schedule end of menu tracking */
3025 wFlags |= TF_ENDMENU;
3026 goto track_menu;
3030 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3032 if (wParam & HTSYSMENU)
3034 /* prevent sysmenu activation for managed windows on Alt down/up */
3035 if (GetPropA( hwnd, "__wine_x11_managed" ))
3036 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3038 else
3040 if( uItem == NO_SELECTED_ITEM )
3041 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3042 else
3043 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3046 track_menu:
3047 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3048 MENU_ExitTracking( hwnd );
3052 /**********************************************************************
3053 * TrackPopupMenu (USER32.@)
3055 * Like the win32 API, the function return the command ID only if the
3056 * flag TPM_RETURNCMD is on.
3059 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3060 INT nReserved, HWND hWnd, const RECT *lpRect )
3062 BOOL ret = FALSE;
3064 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3066 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3067 if (!(wFlags & TPM_NONOTIFY))
3068 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3070 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3071 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3072 MENU_ExitTracking(hWnd);
3074 return ret;
3077 /**********************************************************************
3078 * TrackPopupMenuEx (USER32.@)
3080 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3081 HWND hWnd, LPTPMPARAMS lpTpm )
3083 FIXME("not fully implemented\n" );
3084 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3085 lpTpm ? &lpTpm->rcExclude : NULL );
3088 /***********************************************************************
3089 * PopupMenuWndProc
3091 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3093 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3095 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3097 switch(message)
3099 case WM_CREATE:
3101 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3102 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3103 return 0;
3106 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3107 return MA_NOACTIVATE;
3109 case WM_PAINT:
3111 PAINTSTRUCT ps;
3112 BeginPaint( hwnd, &ps );
3113 MENU_DrawPopupMenu( hwnd, ps.hdc,
3114 (HMENU)GetWindowLongW( hwnd, 0 ) );
3115 EndPaint( hwnd, &ps );
3116 return 0;
3118 case WM_ERASEBKGND:
3119 return 1;
3121 case WM_DESTROY:
3122 /* zero out global pointer in case resident popup window was destroyed. */
3123 if (hwnd == top_popup) top_popup = 0;
3124 break;
3126 case WM_SHOWWINDOW:
3128 if( wParam )
3130 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3132 else
3133 SetWindowLongW( hwnd, 0, 0 );
3134 break;
3136 case MM_SETMENUHANDLE:
3137 SetWindowLongW( hwnd, 0, wParam );
3138 break;
3140 case MM_GETMENUHANDLE:
3141 return GetWindowLongW( hwnd, 0 );
3143 default:
3144 return DefWindowProcW( hwnd, message, wParam, lParam );
3146 return 0;
3150 /***********************************************************************
3151 * MENU_GetMenuBarHeight
3153 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3155 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3156 INT orgX, INT orgY )
3158 HDC hdc;
3159 RECT rectBar;
3160 LPPOPUPMENU lppop;
3162 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3164 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3166 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3167 SelectObject( hdc, hMenuFont);
3168 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3169 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3170 ReleaseDC( hwnd, hdc );
3171 return lppop->Height;
3175 /*******************************************************************
3176 * ChangeMenuA (USER32.@)
3178 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3179 UINT id, UINT flags )
3181 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3182 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3183 id, data );
3184 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3185 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3186 id, data );
3187 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3188 flags & MF_BYPOSITION ? pos : id,
3189 flags & ~MF_REMOVE );
3190 /* Default: MF_INSERT */
3191 return InsertMenuA( hMenu, pos, flags, id, data );
3195 /*******************************************************************
3196 * ChangeMenuW (USER32.@)
3198 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3199 UINT id, UINT flags )
3201 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3202 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3203 id, data );
3204 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3205 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3206 id, data );
3207 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3208 flags & MF_BYPOSITION ? pos : id,
3209 flags & ~MF_REMOVE );
3210 /* Default: MF_INSERT */
3211 return InsertMenuW( hMenu, pos, flags, id, data );
3215 /*******************************************************************
3216 * CheckMenuItem (USER32.@)
3218 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3220 MENUITEM *item;
3221 DWORD ret;
3223 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3224 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3225 ret = item->fState & MF_CHECKED;
3226 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3227 else item->fState &= ~MF_CHECKED;
3228 return ret;
3232 /**********************************************************************
3233 * EnableMenuItem (USER32.@)
3235 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3237 UINT oldflags;
3238 MENUITEM *item;
3239 POPUPMENU *menu;
3241 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3243 /* Get the Popupmenu to access the owner menu */
3244 if (!(menu = MENU_GetMenu(hMenu)))
3245 return (UINT)-1;
3247 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3248 return (UINT)-1;
3250 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3251 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3253 /* If the close item in the system menu change update the close button */
3254 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3256 if (menu->hSysMenuOwner != 0)
3258 RECT rc;
3259 POPUPMENU* parentMenu;
3261 /* Get the parent menu to access*/
3262 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3263 return (UINT)-1;
3265 /* Refresh the frame to reflect the change */
3266 GetWindowRect(parentMenu->hWnd, &rc);
3267 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3268 rc.bottom = 0;
3269 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3273 return oldflags;
3277 /*******************************************************************
3278 * GetMenuStringA (USER32.@)
3280 INT WINAPI GetMenuStringA(
3281 HMENU hMenu, /* [in] menuhandle */
3282 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3283 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3284 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3285 UINT wFlags /* [in] MF_ flags */
3287 MENUITEM *item;
3289 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3290 if (str && nMaxSiz) str[0] = '\0';
3291 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3292 if (!IS_STRING_ITEM(item->fType)) return 0;
3293 if (!str || !nMaxSiz) return strlenW(item->text);
3294 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3295 str[nMaxSiz-1] = 0;
3296 TRACE("returning '%s'\n", str );
3297 return strlen(str);
3301 /*******************************************************************
3302 * GetMenuStringW (USER32.@)
3304 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3305 LPWSTR str, INT nMaxSiz, UINT wFlags )
3307 MENUITEM *item;
3309 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3310 if (str && nMaxSiz) str[0] = '\0';
3311 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3312 if (!IS_STRING_ITEM(item->fType)) return 0;
3313 if (!str || !nMaxSiz) return strlenW(item->text);
3314 lstrcpynW( str, item->text, nMaxSiz );
3315 return strlenW(str);
3319 /**********************************************************************
3320 * HiliteMenuItem (USER32.@)
3322 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3323 UINT wHilite )
3325 LPPOPUPMENU menu;
3326 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3327 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3328 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3329 if (menu->FocusedItem == wItemID) return TRUE;
3330 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3331 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3332 return TRUE;
3336 /**********************************************************************
3337 * GetMenuState (USER32.@)
3339 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3341 MENUITEM *item;
3342 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3343 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3344 debug_print_menuitem (" item: ", item, "");
3345 if (item->fType & MF_POPUP)
3347 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3348 if (!menu) return -1;
3349 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3351 else
3353 /* We used to (from way back then) mask the result to 0xff. */
3354 /* I don't know why and it seems wrong as the documented */
3355 /* return flag MF_SEPARATOR is outside that mask. */
3356 return (item->fType | item->fState);
3361 /**********************************************************************
3362 * GetMenuItemCount (USER32.@)
3364 INT WINAPI GetMenuItemCount( HMENU hMenu )
3366 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3367 if (!menu) return -1;
3368 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3369 return menu->nItems;
3373 /**********************************************************************
3374 * GetMenuItemID (USER32.@)
3376 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3378 MENUITEM * lpmi;
3380 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3381 if (lpmi->fType & MF_POPUP) return -1;
3382 return lpmi->wID;
3387 /*******************************************************************
3388 * InsertMenuW (USER32.@)
3390 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3391 UINT_PTR id, LPCWSTR str )
3393 MENUITEM *item;
3395 if (IS_STRING_ITEM(flags) && str)
3396 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3397 hMenu, pos, flags, id, debugstr_w(str) );
3398 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3399 hMenu, pos, flags, id, (DWORD)str );
3401 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3403 if (!(MENU_SetItemData( item, flags, id, str )))
3405 RemoveMenu( hMenu, pos, flags );
3406 return FALSE;
3409 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3410 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3412 item->hCheckBit = item->hUnCheckBit = 0;
3413 return TRUE;
3417 /*******************************************************************
3418 * InsertMenuA (USER32.@)
3420 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3421 UINT_PTR id, LPCSTR str )
3423 BOOL ret = FALSE;
3425 if (IS_STRING_ITEM(flags) && str)
3427 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3428 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3429 if (newstr)
3431 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3432 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3433 HeapFree( GetProcessHeap(), 0, newstr );
3435 return ret;
3437 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3441 /*******************************************************************
3442 * AppendMenuA (USER32.@)
3444 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3445 UINT_PTR id, LPCSTR data )
3447 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3451 /*******************************************************************
3452 * AppendMenuW (USER32.@)
3454 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3455 UINT_PTR id, LPCWSTR data )
3457 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3461 /**********************************************************************
3462 * RemoveMenu (USER32.@)
3464 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3466 LPPOPUPMENU menu;
3467 MENUITEM *item;
3469 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3470 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3471 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3473 /* Remove item */
3475 MENU_FreeItemData( item );
3477 if (--menu->nItems == 0)
3479 HeapFree( GetProcessHeap(), 0, menu->items );
3480 menu->items = NULL;
3482 else
3484 while(nPos < menu->nItems)
3486 *item = *(item+1);
3487 item++;
3488 nPos++;
3490 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3491 menu->nItems * sizeof(MENUITEM) );
3493 return TRUE;
3497 /**********************************************************************
3498 * DeleteMenu (USER32.@)
3500 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3502 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3503 if (!item) return FALSE;
3504 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3505 /* nPos is now the position of the item */
3506 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3507 return TRUE;
3511 /*******************************************************************
3512 * ModifyMenuW (USER32.@)
3514 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3515 UINT_PTR id, LPCWSTR str )
3517 MENUITEM *item;
3519 if (IS_STRING_ITEM(flags))
3521 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3523 else
3525 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3528 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3529 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3530 return MENU_SetItemData( item, flags, id, str );
3534 /*******************************************************************
3535 * ModifyMenuA (USER32.@)
3537 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3538 UINT_PTR id, LPCSTR str )
3540 BOOL ret = FALSE;
3542 if (IS_STRING_ITEM(flags) && str)
3544 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3545 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3546 if (newstr)
3548 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3549 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3550 HeapFree( GetProcessHeap(), 0, newstr );
3552 return ret;
3554 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3558 /**********************************************************************
3559 * CreatePopupMenu (USER32.@)
3561 HMENU WINAPI CreatePopupMenu(void)
3563 HMENU hmenu;
3564 POPUPMENU *menu;
3566 if (!(hmenu = CreateMenu())) return 0;
3567 menu = MENU_GetMenu( hmenu );
3568 menu->wFlags |= MF_POPUP;
3569 menu->bTimeToHide = FALSE;
3570 return hmenu;
3574 /**********************************************************************
3575 * GetMenuCheckMarkDimensions (USER.417)
3576 * GetMenuCheckMarkDimensions (USER32.@)
3578 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3580 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3584 /**********************************************************************
3585 * SetMenuItemBitmaps (USER32.@)
3587 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3588 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3590 MENUITEM *item;
3591 TRACE("(%p, %04x, %04x, %p, %p)\n",
3592 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3593 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3595 if (!hNewCheck && !hNewUnCheck)
3597 item->fState &= ~MF_USECHECKBITMAPS;
3599 else /* Install new bitmaps */
3601 item->hCheckBit = hNewCheck;
3602 item->hUnCheckBit = hNewUnCheck;
3603 item->fState |= MF_USECHECKBITMAPS;
3605 return TRUE;
3609 /**********************************************************************
3610 * CreateMenu (USER32.@)
3612 HMENU WINAPI CreateMenu(void)
3614 HMENU hMenu;
3615 LPPOPUPMENU menu;
3616 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3617 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3619 ZeroMemory(menu, sizeof(POPUPMENU));
3620 menu->wMagic = MENU_MAGIC;
3621 menu->FocusedItem = NO_SELECTED_ITEM;
3622 menu->bTimeToHide = FALSE;
3624 TRACE("return %p\n", hMenu );
3626 return hMenu;
3630 /**********************************************************************
3631 * DestroyMenu (USER32.@)
3633 BOOL WINAPI DestroyMenu( HMENU hMenu )
3635 TRACE("(%p)\n", hMenu);
3637 /* Silently ignore attempts to destroy default system popup */
3639 if (hMenu && hMenu != MENU_DefSysPopup)
3641 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3643 if (!lppop) return FALSE;
3645 lppop->wMagic = 0; /* Mark it as destroyed */
3647 /* DestroyMenu should not destroy system menu popup owner */
3648 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3650 DestroyWindow( lppop->hWnd );
3651 lppop->hWnd = 0;
3654 if (lppop->items) /* recursively destroy submenus */
3656 int i;
3657 MENUITEM *item = lppop->items;
3658 for (i = lppop->nItems; i > 0; i--, item++)
3660 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3661 MENU_FreeItemData( item );
3663 HeapFree( GetProcessHeap(), 0, lppop->items );
3665 USER_HEAP_FREE( hMenu );
3667 return (hMenu != MENU_DefSysPopup);
3671 /**********************************************************************
3672 * GetSystemMenu (USER32.@)
3674 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3676 WND *wndPtr = WIN_GetPtr( hWnd );
3677 HMENU retvalue = 0;
3679 if (wndPtr == WND_DESKTOP) return 0;
3680 if (wndPtr == WND_OTHER_PROCESS)
3682 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3684 else if (wndPtr)
3686 if( wndPtr->hSysMenu )
3688 if( bRevert )
3690 DestroyMenu(wndPtr->hSysMenu);
3691 wndPtr->hSysMenu = 0;
3693 else
3695 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3696 if( menu )
3698 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3699 menu->items[0].hSubMenu = MENU_CopySysPopup();
3701 else
3703 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3704 wndPtr->hSysMenu, hWnd);
3705 wndPtr->hSysMenu = 0;
3710 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3711 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3713 if( wndPtr->hSysMenu )
3715 POPUPMENU *menu;
3716 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3718 /* Store the dummy sysmenu handle to facilitate the refresh */
3719 /* of the close button if the SC_CLOSE item change */
3720 menu = MENU_GetMenu(retvalue);
3721 if ( menu )
3722 menu->hSysMenuOwner = wndPtr->hSysMenu;
3724 WIN_ReleasePtr( wndPtr );
3726 return bRevert ? 0 : retvalue;
3730 /*******************************************************************
3731 * SetSystemMenu (USER32.@)
3733 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3735 WND *wndPtr = WIN_GetPtr( hwnd );
3737 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3739 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3740 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3741 WIN_ReleasePtr( wndPtr );
3742 return TRUE;
3744 return FALSE;
3748 /**********************************************************************
3749 * GetMenu (USER32.@)
3751 HMENU WINAPI GetMenu( HWND hWnd )
3753 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3754 TRACE("for %p returning %p\n", hWnd, retvalue);
3755 return retvalue;
3758 /**********************************************************************
3759 * GetMenuBarInfo (USER32.@)
3761 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
3763 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
3764 return FALSE;
3767 /**********************************************************************
3768 * MENU_SetMenu
3770 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3771 * SetWindowPos call that would result if SetMenu were called directly.
3773 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3775 TRACE("(%p, %p);\n", hWnd, hMenu);
3777 if (hMenu && !IsMenu(hMenu))
3779 WARN("hMenu %p is not a menu handle\n", hMenu);
3780 return FALSE;
3782 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3783 return FALSE;
3785 hWnd = WIN_GetFullHandle( hWnd );
3786 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3788 if (hMenu != 0)
3790 LPPOPUPMENU lpmenu;
3792 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3794 lpmenu->hWnd = hWnd;
3795 lpmenu->Height = 0; /* Make sure we recalculate the size */
3797 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3798 return TRUE;
3802 /**********************************************************************
3803 * SetMenu (USER32.@)
3805 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3807 if(!MENU_SetMenu(hWnd, hMenu))
3808 return FALSE;
3810 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3811 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3812 return TRUE;
3816 /**********************************************************************
3817 * GetSubMenu (USER32.@)
3819 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3821 MENUITEM * lpmi;
3823 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3824 if (!(lpmi->fType & MF_POPUP)) return 0;
3825 return lpmi->hSubMenu;
3829 /**********************************************************************
3830 * DrawMenuBar (USER32.@)
3832 BOOL WINAPI DrawMenuBar( HWND hWnd )
3834 LPPOPUPMENU lppop;
3835 HMENU hMenu = GetMenu(hWnd);
3837 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3838 return FALSE;
3839 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3841 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3842 lppop->hwndOwner = hWnd;
3843 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3844 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3845 return TRUE;
3848 /***********************************************************************
3849 * DrawMenuBarTemp (USER32.@)
3851 * UNDOCUMENTED !!
3853 * called by W98SE desk.cpl Control Panel Applet
3855 * Not 100% sure about the param names, but close.
3857 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3859 LPPOPUPMENU lppop;
3860 UINT i,retvalue;
3861 HFONT hfontOld = 0;
3863 if (!hMenu)
3864 hMenu = GetMenu(hwnd);
3866 if (!hFont)
3867 hFont = hMenuFont;
3869 lppop = MENU_GetMenu( hMenu );
3870 if (lppop == NULL || lprect == NULL)
3872 retvalue = GetSystemMetrics(SM_CYMENU);
3873 goto END;
3876 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3878 hfontOld = SelectObject( hDC, hFont);
3880 if (lppop->Height == 0)
3881 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3883 lprect->bottom = lprect->top + lppop->Height;
3885 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3887 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3888 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3889 LineTo( hDC, lprect->right, lprect->bottom );
3891 if (lppop->nItems == 0)
3893 retvalue = GetSystemMetrics(SM_CYMENU);
3894 goto END;
3897 for (i = 0; i < lppop->nItems; i++)
3899 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3900 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3902 retvalue = lppop->Height;
3904 END:
3905 if (hfontOld) SelectObject (hDC, hfontOld);
3906 return retvalue;
3909 /***********************************************************************
3910 * EndMenu (USER.187)
3911 * EndMenu (USER32.@)
3913 void WINAPI EndMenu(void)
3915 /* if we are in the menu code, and it is active */
3916 if (!fEndMenu && top_popup)
3918 /* terminate the menu handling code */
3919 fEndMenu = TRUE;
3921 /* needs to be posted to wakeup the internal menu handler */
3922 /* which will now terminate the menu, in the event that */
3923 /* the main window was minimized, or lost focus, so we */
3924 /* don't end up with an orphaned menu */
3925 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3930 /***********************************************************************
3931 * LookupMenuHandle (USER.217)
3933 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3935 HMENU hmenu32 = HMENU_32(hmenu);
3936 UINT id32 = id;
3937 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3938 else return HMENU_16(hmenu32);
3942 /**********************************************************************
3943 * LoadMenu (USER.150)
3945 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3947 HRSRC16 hRsrc;
3948 HGLOBAL16 handle;
3949 HMENU16 hMenu;
3951 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3952 if (!name) return 0;
3954 instance = GetExePtr( instance );
3955 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3956 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3957 hMenu = LoadMenuIndirect16(LockResource16(handle));
3958 FreeResource16( handle );
3959 return hMenu;
3963 /*****************************************************************
3964 * LoadMenuA (USER32.@)
3966 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3968 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3969 if (!hrsrc) return 0;
3970 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3974 /*****************************************************************
3975 * LoadMenuW (USER32.@)
3977 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3979 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3980 if (!hrsrc) return 0;
3981 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3985 /**********************************************************************
3986 * LoadMenuIndirect (USER.220)
3988 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3990 HMENU hMenu;
3991 WORD version, offset;
3992 LPCSTR p = (LPCSTR)template;
3994 TRACE("(%p)\n", template );
3995 version = GET_WORD(p);
3996 p += sizeof(WORD);
3997 if (version)
3999 WARN("version must be 0 for Win16\n" );
4000 return 0;
4002 offset = GET_WORD(p);
4003 p += sizeof(WORD) + offset;
4004 if (!(hMenu = CreateMenu())) return 0;
4005 if (!MENU_ParseResource( p, hMenu, FALSE ))
4007 DestroyMenu( hMenu );
4008 return 0;
4010 return HMENU_16(hMenu);
4014 /**********************************************************************
4015 * LoadMenuIndirectW (USER32.@)
4017 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4019 HMENU hMenu;
4020 WORD version, offset;
4021 LPCSTR p = (LPCSTR)template;
4023 version = GET_WORD(p);
4024 p += sizeof(WORD);
4025 TRACE("%p, ver %d\n", template, version );
4026 switch (version)
4028 case 0: /* standard format is version of 0 */
4029 offset = GET_WORD(p);
4030 p += sizeof(WORD) + offset;
4031 if (!(hMenu = CreateMenu())) return 0;
4032 if (!MENU_ParseResource( p, hMenu, TRUE ))
4034 DestroyMenu( hMenu );
4035 return 0;
4037 return hMenu;
4038 case 1: /* extended format is version of 1 */
4039 offset = GET_WORD(p);
4040 p += sizeof(WORD) + offset;
4041 if (!(hMenu = CreateMenu())) return 0;
4042 if (!MENUEX_ParseResource( p, hMenu))
4044 DestroyMenu( hMenu );
4045 return 0;
4047 return hMenu;
4048 default:
4049 ERR("version %d not supported.\n", version);
4050 return 0;
4055 /**********************************************************************
4056 * LoadMenuIndirectA (USER32.@)
4058 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4060 return LoadMenuIndirectW( template );
4064 /**********************************************************************
4065 * IsMenu (USER32.@)
4067 BOOL WINAPI IsMenu(HMENU hmenu)
4069 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4070 return menu != NULL;
4073 /**********************************************************************
4074 * GetMenuItemInfo_common
4077 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4078 LPMENUITEMINFOW lpmii, BOOL unicode)
4080 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4082 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4084 if (!menu)
4085 return FALSE;
4087 if (lpmii->fMask & MIIM_TYPE) {
4088 lpmii->fType = menu->fType;
4089 switch (MENU_ITEM_TYPE(menu->fType)) {
4090 case MF_STRING:
4091 break; /* will be done below */
4092 case MF_OWNERDRAW:
4093 case MF_BITMAP:
4094 lpmii->dwTypeData = menu->text;
4095 /* fall through */
4096 default:
4097 lpmii->cch = 0;
4101 /* copy the text string */
4102 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4103 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4105 int len;
4106 if (unicode)
4108 len = strlenW(menu->text);
4109 if(lpmii->dwTypeData && lpmii->cch)
4110 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4112 else
4114 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4115 if(lpmii->dwTypeData && lpmii->cch)
4116 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4117 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4118 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4120 /* if we've copied a substring we return its length */
4121 if(lpmii->dwTypeData && lpmii->cch)
4123 if (lpmii->cch <= len) lpmii->cch--;
4125 else /* return length of string */
4126 lpmii->cch = len;
4129 if (lpmii->fMask & MIIM_FTYPE)
4130 lpmii->fType = menu->fType;
4132 if (lpmii->fMask & MIIM_BITMAP)
4133 lpmii->hbmpItem = menu->hbmpItem;
4135 if (lpmii->fMask & MIIM_STATE)
4136 lpmii->fState = menu->fState;
4138 if (lpmii->fMask & MIIM_ID)
4139 lpmii->wID = menu->wID;
4141 if (lpmii->fMask & MIIM_SUBMENU)
4142 lpmii->hSubMenu = menu->hSubMenu;
4144 if (lpmii->fMask & MIIM_CHECKMARKS) {
4145 lpmii->hbmpChecked = menu->hCheckBit;
4146 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4148 if (lpmii->fMask & MIIM_DATA)
4149 lpmii->dwItemData = menu->dwItemData;
4151 return TRUE;
4154 /**********************************************************************
4155 * GetMenuItemInfoA (USER32.@)
4157 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4158 LPMENUITEMINFOA lpmii)
4160 return GetMenuItemInfo_common (hmenu, item, bypos,
4161 (LPMENUITEMINFOW)lpmii, FALSE);
4164 /**********************************************************************
4165 * GetMenuItemInfoW (USER32.@)
4167 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4168 LPMENUITEMINFOW lpmii)
4170 return GetMenuItemInfo_common (hmenu, item, bypos,
4171 lpmii, TRUE);
4175 /* set a menu item text from a ASCII or Unicode string */
4176 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4178 if (!text)
4180 menu->text = NULL;
4181 menu->fType |= MF_SEPARATOR;
4183 else if (unicode)
4185 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4186 strcpyW( menu->text, text );
4188 else
4190 LPCSTR str = (LPCSTR)text;
4191 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4192 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4193 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4198 /**********************************************************************
4199 * SetMenuItemInfo_common
4202 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4203 const MENUITEMINFOW *lpmii,
4204 BOOL unicode)
4206 if (!menu) return FALSE;
4208 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4210 if (lpmii->fMask & MIIM_TYPE ) {
4211 /* Get rid of old string. */
4212 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4213 HeapFree(GetProcessHeap(), 0, menu->text);
4214 menu->text = NULL;
4217 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4218 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4219 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4221 menu->text = lpmii->dwTypeData;
4223 if (IS_STRING_ITEM(menu->fType))
4224 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4227 if (lpmii->fMask & MIIM_FTYPE ) {
4228 /* free the string when the type is changing */
4229 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4230 HeapFree(GetProcessHeap(), 0, menu->text);
4231 menu->text = NULL;
4233 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4234 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4235 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4236 menu->fType |= MF_SEPARATOR;
4239 if (lpmii->fMask & MIIM_STRING ) {
4240 if (IS_STRING_ITEM(menu->fType)) {
4241 /* free the string when used */
4242 HeapFree(GetProcessHeap(), 0, menu->text);
4243 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4247 if (lpmii->fMask & MIIM_STATE)
4249 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4250 menu->fState = lpmii->fState;
4253 if (lpmii->fMask & MIIM_ID)
4254 menu->wID = lpmii->wID;
4256 if (lpmii->fMask & MIIM_SUBMENU) {
4257 menu->hSubMenu = lpmii->hSubMenu;
4258 if (menu->hSubMenu) {
4259 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4260 if (subMenu) {
4261 subMenu->wFlags |= MF_POPUP;
4262 menu->fType |= MF_POPUP;
4264 else
4265 /* FIXME: Return an error ? */
4266 menu->fType &= ~MF_POPUP;
4268 else
4269 menu->fType &= ~MF_POPUP;
4272 if (lpmii->fMask & MIIM_CHECKMARKS)
4274 if (lpmii->fType & MFT_RADIOCHECK)
4275 menu->fType |= MFT_RADIOCHECK;
4277 menu->hCheckBit = lpmii->hbmpChecked;
4278 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4280 if (lpmii->fMask & MIIM_DATA)
4281 menu->dwItemData = lpmii->dwItemData;
4283 if (lpmii->fMask & MIIM_BITMAP)
4284 menu->hbmpItem = lpmii->hbmpItem;
4286 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4287 return TRUE;
4290 /**********************************************************************
4291 * SetMenuItemInfoA (USER32.@)
4293 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4294 const MENUITEMINFOA *lpmii)
4296 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4297 (const MENUITEMINFOW *)lpmii, FALSE);
4300 /**********************************************************************
4301 * SetMenuItemInfoW (USER32.@)
4303 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4304 const MENUITEMINFOW *lpmii)
4306 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4307 lpmii, TRUE);
4310 /**********************************************************************
4311 * SetMenuDefaultItem (USER32.@)
4314 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4316 UINT i;
4317 POPUPMENU *menu;
4318 MENUITEM *item;
4320 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4322 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4324 /* reset all default-item flags */
4325 item = menu->items;
4326 for (i = 0; i < menu->nItems; i++, item++)
4328 item->fState &= ~MFS_DEFAULT;
4331 /* no default item */
4332 if ( -1 == uItem)
4334 return TRUE;
4337 item = menu->items;
4338 if ( bypos )
4340 if ( uItem >= menu->nItems ) return FALSE;
4341 item[uItem].fState |= MFS_DEFAULT;
4342 return TRUE;
4344 else
4346 for (i = 0; i < menu->nItems; i++, item++)
4348 if (item->wID == uItem)
4350 item->fState |= MFS_DEFAULT;
4351 return TRUE;
4356 return FALSE;
4359 /**********************************************************************
4360 * GetMenuDefaultItem (USER32.@)
4362 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4364 POPUPMENU *menu;
4365 MENUITEM * item;
4366 UINT i = 0;
4368 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4370 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4372 /* find default item */
4373 item = menu->items;
4375 /* empty menu */
4376 if (! item) return -1;
4378 while ( !( item->fState & MFS_DEFAULT ) )
4380 i++; item++;
4381 if (i >= menu->nItems ) return -1;
4384 /* default: don't return disabled items */
4385 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4387 /* search rekursiv when needed */
4388 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4390 UINT ret;
4391 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4392 if ( -1 != ret ) return ret;
4394 /* when item not found in submenu, return the popup item */
4396 return ( bypos ) ? i : item->wID;
4401 /**********************************************************************
4402 * InsertMenuItemA (USER32.@)
4404 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4405 const MENUITEMINFOA *lpmii)
4407 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4408 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4412 /**********************************************************************
4413 * InsertMenuItemW (USER32.@)
4415 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4416 const MENUITEMINFOW *lpmii)
4418 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4419 return SetMenuItemInfo_common(item, lpmii, TRUE);
4422 /**********************************************************************
4423 * CheckMenuRadioItem (USER32.@)
4426 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4427 UINT first, UINT last, UINT check,
4428 UINT bypos)
4430 MENUITEM *mifirst, *milast, *micheck;
4431 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4433 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4435 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4436 milast = MENU_FindItem (&mlast, &last, bypos);
4437 micheck = MENU_FindItem (&mcheck, &check, bypos);
4439 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4440 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4441 micheck > milast || micheck < mifirst)
4442 return FALSE;
4444 while (mifirst <= milast)
4446 if (mifirst == micheck)
4448 mifirst->fType |= MFT_RADIOCHECK;
4449 mifirst->fState |= MFS_CHECKED;
4450 } else {
4451 mifirst->fType &= ~MFT_RADIOCHECK;
4452 mifirst->fState &= ~MFS_CHECKED;
4454 mifirst++;
4457 return TRUE;
4461 /**********************************************************************
4462 * GetMenuItemRect (USER32.@)
4464 * ATTENTION: Here, the returned values in rect are the screen
4465 * coordinates of the item just like if the menu was
4466 * always on the upper left side of the application.
4469 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4470 LPRECT rect)
4472 POPUPMENU *itemMenu;
4473 MENUITEM *item;
4474 HWND referenceHwnd;
4476 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4478 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4479 referenceHwnd = hwnd;
4481 if(!hwnd)
4483 itemMenu = MENU_GetMenu(hMenu);
4484 if (itemMenu == NULL)
4485 return FALSE;
4487 if(itemMenu->hWnd == 0)
4488 return FALSE;
4489 referenceHwnd = itemMenu->hWnd;
4492 if ((rect == NULL) || (item == NULL))
4493 return FALSE;
4495 *rect = item->rect;
4497 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4499 return TRUE;
4503 /**********************************************************************
4504 * SetMenuInfo (USER32.@)
4506 * FIXME
4507 * MIM_APPLYTOSUBMENUS
4508 * actually use the items to draw the menu
4510 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4512 POPUPMENU *menu;
4514 TRACE("(%p %p)\n", hMenu, lpmi);
4516 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4519 if (lpmi->fMask & MIM_BACKGROUND)
4520 menu->hbrBack = lpmi->hbrBack;
4522 if (lpmi->fMask & MIM_HELPID)
4523 menu->dwContextHelpID = lpmi->dwContextHelpID;
4525 if (lpmi->fMask & MIM_MAXHEIGHT)
4526 menu->cyMax = lpmi->cyMax;
4528 if (lpmi->fMask & MIM_MENUDATA)
4529 menu->dwMenuData = lpmi->dwMenuData;
4531 if (lpmi->fMask & MIM_STYLE)
4533 menu->dwStyle = lpmi->dwStyle;
4534 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4535 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4536 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4537 if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4538 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4541 return TRUE;
4543 return FALSE;
4546 /**********************************************************************
4547 * GetMenuInfo (USER32.@)
4549 * NOTES
4550 * win98/NT5.0
4553 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4554 { POPUPMENU *menu;
4556 TRACE("(%p %p)\n", hMenu, lpmi);
4558 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4561 if (lpmi->fMask & MIM_BACKGROUND)
4562 lpmi->hbrBack = menu->hbrBack;
4564 if (lpmi->fMask & MIM_HELPID)
4565 lpmi->dwContextHelpID = menu->dwContextHelpID;
4567 if (lpmi->fMask & MIM_MAXHEIGHT)
4568 lpmi->cyMax = menu->cyMax;
4570 if (lpmi->fMask & MIM_MENUDATA)
4571 lpmi->dwMenuData = menu->dwMenuData;
4573 if (lpmi->fMask & MIM_STYLE)
4574 lpmi->dwStyle = menu->dwStyle;
4576 return TRUE;
4578 return FALSE;
4582 /**********************************************************************
4583 * SetMenuContextHelpId (USER32.@)
4585 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4587 LPPOPUPMENU menu;
4589 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4591 if ((menu = MENU_GetMenu(hMenu)))
4593 menu->dwContextHelpID = dwContextHelpID;
4594 return TRUE;
4596 return FALSE;
4600 /**********************************************************************
4601 * GetMenuContextHelpId (USER32.@)
4603 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4605 LPPOPUPMENU menu;
4607 TRACE("(%p)\n", hMenu);
4609 if ((menu = MENU_GetMenu(hMenu)))
4611 return menu->dwContextHelpID;
4613 return 0;
4616 /**********************************************************************
4617 * MenuItemFromPoint (USER32.@)
4619 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4621 POPUPMENU *menu = MENU_GetMenu(hMenu);
4622 UINT pos;
4624 /*FIXME: Do we have to handle hWnd here? */
4625 if (!menu) return -1;
4626 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4627 return pos;
4631 /**********************************************************************
4632 * translate_accelerator
4634 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4635 BYTE fVirt, WORD key, WORD cmd )
4637 INT mask = 0;
4638 UINT mesg = 0;
4640 if (wParam != key) return FALSE;
4642 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4643 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4644 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4646 if (message == WM_CHAR || message == WM_SYSCHAR)
4648 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4650 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4651 goto found;
4654 else
4656 if(fVirt & FVIRTKEY)
4658 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4659 wParam, 0xff & HIWORD(lParam));
4661 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4662 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4664 else
4666 if (!(lParam & 0x01000000)) /* no special_key */
4668 if ((fVirt & FALT) && (lParam & 0x20000000))
4669 { /* ^^ ALT pressed */
4670 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4671 goto found;
4676 return FALSE;
4678 found:
4679 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4680 mesg = 1;
4681 else
4683 HMENU hMenu, hSubMenu, hSysMenu;
4684 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4686 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4687 hSysMenu = get_win_sys_menu( hWnd );
4689 /* find menu item and ask application to initialize it */
4690 /* 1. in the system menu */
4691 hSubMenu = hSysMenu;
4692 nPos = cmd;
4693 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4695 if (GetCapture())
4696 mesg = 2;
4697 if (!IsWindowEnabled(hWnd))
4698 mesg = 3;
4699 else
4701 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4702 if(hSubMenu != hSysMenu)
4704 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4705 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4706 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4708 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4711 else /* 2. in the window's menu */
4713 hSubMenu = hMenu;
4714 nPos = cmd;
4715 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4717 if (GetCapture())
4718 mesg = 2;
4719 if (!IsWindowEnabled(hWnd))
4720 mesg = 3;
4721 else
4723 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4724 if(hSubMenu != hMenu)
4726 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4727 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4728 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4730 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4735 if (mesg == 0)
4737 if (uSysStat != (UINT)-1)
4739 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4740 mesg=4;
4741 else
4742 mesg=WM_SYSCOMMAND;
4744 else
4746 if (uStat != (UINT)-1)
4748 if (IsIconic(hWnd))
4749 mesg=5;
4750 else
4752 if (uStat & (MF_DISABLED|MF_GRAYED))
4753 mesg=6;
4754 else
4755 mesg=WM_COMMAND;
4758 else
4759 mesg=WM_COMMAND;
4764 if( mesg==WM_COMMAND )
4766 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4767 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4769 else if( mesg==WM_SYSCOMMAND )
4771 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4772 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4774 else
4776 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4777 * #0: unknown (please report!)
4778 * #1: for WM_KEYUP,WM_SYSKEYUP
4779 * #2: mouse is captured
4780 * #3: window is disabled
4781 * #4: it's a disabled system menu option
4782 * #5: it's a menu option, but window is iconic
4783 * #6: it's a menu option, but disabled
4785 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4786 if(mesg==0)
4787 ERR_(accel)(" unknown reason - please report!\n");
4789 return TRUE;
4792 /**********************************************************************
4793 * TranslateAccelerator (USER32.@)
4794 * TranslateAcceleratorA (USER32.@)
4796 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4798 /* YES, Accel16! */
4799 LPACCEL16 lpAccelTbl;
4800 int i;
4801 WPARAM wParam;
4803 if (!hWnd || !msg) return 0;
4805 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4807 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4808 return 0;
4811 wParam = msg->wParam;
4813 switch (msg->message)
4815 case WM_KEYDOWN:
4816 case WM_SYSKEYDOWN:
4817 break;
4819 case WM_CHAR:
4820 case WM_SYSCHAR:
4822 char ch = LOWORD(wParam);
4823 WCHAR wch;
4824 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4825 wParam = MAKEWPARAM(wch, HIWORD(wParam));
4827 break;
4829 default:
4830 return 0;
4833 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4834 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4835 i = 0;
4838 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4839 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4840 return 1;
4841 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4843 return 0;
4846 /**********************************************************************
4847 * TranslateAcceleratorW (USER32.@)
4849 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4851 /* YES, Accel16! */
4852 LPACCEL16 lpAccelTbl;
4853 int i;
4855 if (!hWnd || !msg) return 0;
4857 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4859 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4860 return 0;
4863 switch (msg->message)
4865 case WM_KEYDOWN:
4866 case WM_SYSKEYDOWN:
4867 case WM_CHAR:
4868 case WM_SYSCHAR:
4869 break;
4871 default:
4872 return 0;
4875 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4876 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4877 i = 0;
4880 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4881 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4882 return 1;
4883 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4885 return 0;